/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/usr/secureboot/trusted/trustedboot.C $ */ /* */ /* OpenPOWER HostBoot Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2015,2018 */ /* [+] 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 trustedboot.C * * @brief Trusted boot interfaces */ // ---------------------------------------------- // Includes // ---------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_BMC_IPMI #include #endif #include #include #include #include "trustedboot.H" #include "trustedTypes.H" #include "trustedbootCmds.H" #include "trustedbootUtils.H" #include "tpmLogMgr.H" #include "base/trustedbootMsg.H" #include #ifdef CONFIG_DRTM #include #endif #include #include #include #include #include #include #include namespace TRUSTEDBOOT { extern SystemData systemData; errlHndl_t getTpmLogDevtreeInfo( const TpmTarget* const i_pTpm, uint64_t & io_logAddr, size_t & o_allocationSize, uint64_t & o_xscomAddr, uint32_t & o_i2cMasterOffset) { assert(i_pTpm != nullptr,"getTpmLogDevtreeInfo: BUG! i_pTpm was nullptr"); assert(i_pTpm->getAttr() == TARGETING::TYPE_TPM, "getTpmLogDevtreeInfo: BUG! Expected target to be of TPM type, but " "it was of type 0x%08X",i_pTpm->getAttr()); errlHndl_t err = nullptr; auto * const pTpmLogMgr = getTpmLogMgr(i_pTpm); TRACUCOMP( g_trac_trustedboot, ENTER_MRK"getTpmLogDevtreeInfo() tgt=0x%08X Addr:0x%016lX " "0x%016lX", TARGETING::get_huid(i_pTpm), io_logAddr ,reinterpret_cast(pTpmLogMgr)); o_allocationSize = 0; auto hwasState = i_pTpm->getAttr(); if (nullptr != pTpmLogMgr && hwasState.present) { err = TpmLogMgr_getDevtreeInfo(pTpmLogMgr, io_logAddr, o_allocationSize, o_xscomAddr, o_i2cMasterOffset); } TRACUCOMP( g_trac_trustedboot, EXIT_MRK"getTpmLogDevtreeInfo() Addr:0x%016lX",io_logAddr); return err; } void setTpmDevtreeInfo( const TpmTarget* const i_pTpm, const uint64_t i_xscomAddr, const uint32_t i_i2cMasterOffset) { assert(i_pTpm != nullptr,"setTpmLogDevtreeInfo: BUG! i_pTpm was nullptr"); assert(i_pTpm->getAttr() == TARGETING::TYPE_TPM, "setTpmLogDevtreeInfo: BUG! Expected target to be of TPM type, but " "it was of type 0x%08X",i_pTpm->getAttr()); TRACUCOMP( g_trac_trustedboot, ENTER_MRK"setTpmLogDevtreeOffset() tgt=0x%08X " "Xscom:0x%016lX Master:0x%08X", TARGETING::get_huid(i_pTpm), i_xscomAddr, i_i2cMasterOffset); auto * const pTpmLogMgr = getTpmLogMgr(i_pTpm); if (nullptr != pTpmLogMgr) { TpmLogMgr_setTpmDevtreeInfo(pTpmLogMgr, i_xscomAddr, i_i2cMasterOffset); } } void getTpmWithRoleOf( TARGETING::TPM_ROLE const i_tpmRole, TARGETING::Target*& o_pTpm) { o_pTpm = nullptr; TARGETING::TargetHandleList tpmList; getTPMs(tpmList,TPM_FILTER::ALL_IN_BLUEPRINT); TARGETING::PredicateAttrVal hasMatchingTpmRole(i_tpmRole); auto itr = std::find_if(tpmList.begin(),tpmList.end(), [&hasMatchingTpmRole](const TARGETING::Target* const i_pTpm) { return hasMatchingTpmRole(i_pTpm); }); if(itr!=tpmList.end()) { o_pTpm=*itr; } } void getPrimaryTpm(TARGETING::Target*& o_pPrimaryTpm) { getTpmWithRoleOf(TARGETING::TPM_ROLE_TPM_PRIMARY, o_pPrimaryTpm); } void getBackupTpm(TARGETING::Target*& o_pBackupTpm) { getTpmWithRoleOf(TARGETING::TPM_ROLE_TPM_BACKUP, o_pBackupTpm); } bool functionalPrimaryTpmExists() { bool exists = false; #ifdef CONFIG_TPMDD TARGETING::TargetHandleList tpmList; getTPMs(tpmList,TPM_FILTER::ALL_IN_BLUEPRINT); TARGETING::PredicateHwas presentAndFunctional; presentAndFunctional.present(true); presentAndFunctional.functional(true); TARGETING::PredicateAttrVal initialized(true); // Only look for primary TPM TARGETING::PredicateAttrVal isPrimaryTpm(TARGETING::TPM_ROLE_TPM_PRIMARY); auto itr = std::find_if(tpmList.begin(),tpmList.end(), [&presentAndFunctional, &initialized, &isPrimaryTpm]( const TARGETING::Target* const i_pTpm) { return (isPrimaryTpm(i_pTpm) && (presentAndFunctional(i_pTpm) || !initialized(i_pTpm))); }); exists = (itr!=tpmList.end()) ? true : false; #endif return exists; } void* host_update_master_tpm( void *io_pArgs ) { errlHndl_t err = nullptr; bool unlock = false; TRACDCOMP( g_trac_trustedboot, ENTER_MRK"host_update_master_tpm()" ); TRACUCOMP( g_trac_trustedboot, ENTER_MRK"host_update_master_tpm()"); // Get all TPMs to setup our array TARGETING::TargetHandleList tpmList; getTPMs(tpmList,TPM_FILTER::ALL_IN_BLUEPRINT); // Currently we only support a MAX of two TPMS per node assert(tpmList.size() <= MAX_TPMS_PER_NODE, "Too many TPMs found"); do { if (tpmList.empty()) { TRACFCOMP( g_trac_trustedboot, "No TPM Targets found"); break; } TARGETING::TargetService& tS = TARGETING::targetService(); TARGETING::Target* procTarget = nullptr; err = tS.queryMasterProcChipTargetHandle( procTarget ); if (nullptr != err) { break; } for(auto tpm : tpmList) { mutex_lock(tpm->getHbMutexAttr()); } unlock = true; // Loop through the TPMs and figure out if they are attached // to the master or alternate master processor TPMDD::tpm_info_t tpmData; for (auto tpm : tpmList) { memset(&tpmData, 0, sizeof(tpmData)); errlHndl_t readErr = tpmReadAttributes(tpm, tpmData, TPM_LOCALITY_0); if (nullptr != readErr) { // We are just looking for configured TPMs here // so we ignore any errors delete readErr; readErr = nullptr; } else { // If TPM connected to acting master processor, it is // primary; otherwise it is backup TARGETING::TPM_ROLE tpmRole = (tpmData.i2cTarget == procTarget) ? TARGETING::TPM_ROLE_TPM_PRIMARY : TARGETING::TPM_ROLE_TPM_BACKUP; tpm->setAttr(tpmRole); } } // Initialize primary TPM TARGETING::Target* pPrimaryTpm = nullptr; (void)getPrimaryTpm(pPrimaryTpm); if(pPrimaryTpm) { auto hwasState = pPrimaryTpm->getAttr(); if( hwasState.present && hwasState.functional) { // API call will set TPM init attempted appropriately tpmInitialize(pPrimaryTpm); } else { pPrimaryTpm->setAttr< TARGETING::ATTR_HB_TPM_INIT_ATTEMPTED>(true); } // Allocate TPM log if it hasn't been already auto pTpmLogMgr = getTpmLogMgr(pPrimaryTpm); // Need to grab state again, as it could have changed hwasState = pPrimaryTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); if( hwasState.present && hwasState.functional && nullptr == pTpmLogMgr) { // @todo RTC:145689 For DRTM we locate the previous SRTM log // and reuse. We must also allocate a DRTM log to be used pTpmLogMgr = new TpmLogMgr; setTpmLogMgr(pPrimaryTpm,pTpmLogMgr); err = TpmLogMgr_initialize(pTpmLogMgr); if (nullptr != err) { hwasState = pPrimaryTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); hwasState.functional = false; pPrimaryTpm->setAttr( hwasState); break; } } } bool primaryTpmAvail = (pPrimaryTpm != nullptr); if(primaryTpmAvail) { auto primaryHwasState = pPrimaryTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); if (!primaryHwasState.functional || !primaryHwasState.present) { primaryTpmAvail = false; pPrimaryTpm->setAttr(true); } } if(!primaryTpmAvail) { // Primary TPM not available TRACFCOMP( g_trac_trustedboot, "Primary TPM Existence Fail"); } TARGETING::Target* pBackupTpm = nullptr; getBackupTpm(pBackupTpm); if(pBackupTpm == nullptr) { TRACFCOMP( g_trac_trustedboot, "host_update_master_tpm() " "Backup TPM unavailable " "since it's not in the system blueprint."); } else { auto l_backupHwasState = pBackupTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); TPMDD::tpm_info_t tpmInfo; memset(&tpmInfo, 0, sizeof(tpmInfo)); errlHndl_t tmpErr = TPMDD::tpmReadAttributes( pBackupTpm, tpmInfo, TPM_LOCALITY_0); if (nullptr != tmpErr || !tpmInfo.tpmEnabled || (l_backupHwasState.functional && l_backupHwasState.present)) // If the backup state is functional and present then we are // in MPIPL scenario and we need to reset the states { TRACUCOMP( g_trac_trustedboot, "host_update_master_tpm() " "Marking backup TPM unavailable"); l_backupHwasState.present = false; l_backupHwasState.functional = false; pBackupTpm->setAttr< TARGETING::ATTR_HWAS_STATE>(l_backupHwasState); if (nullptr != tmpErr) { // Ignore attribute read failure delete tmpErr; tmpErr = nullptr; } } } } while ( 0 ); if( unlock ) { for(auto tpm : tpmList) { mutex_unlock(tpm->getHbMutexAttr< TARGETING::ATTR_HB_TPM_MUTEX>()); } unlock = false; } // Make sure we are in a state // where we have a functional TPM TRUSTEDBOOT::tpmVerifyFunctionalPrimaryTpmExists(); if (nullptr == err) { // Start the task to start to handle the message queue/extends task_create(&TRUSTEDBOOT::tpmDaemon, nullptr); } TARGETING::Target* pPrimaryTpm = nullptr; (void)getPrimaryTpm(pPrimaryTpm); if (nullptr == err) { // Log config entries to TPM - needs to be after mutex_unlock if(pPrimaryTpm) { err = tpmLogConfigEntries(pPrimaryTpm); } } if(pPrimaryTpm) { TRACUCOMP( g_trac_trustedboot, "host_update_master_tpm() - " "Primary TPM Present:%d Functional:%d Init Attempted:%d" " Usable:%d", pPrimaryTpm->getAttr(). present, pPrimaryTpm->getAttr(). functional, pPrimaryTpm->getAttr< TARGETING::ATTR_HB_TPM_INIT_ATTEMPTED>(), !(pPrimaryTpm->getAttr())); } TARGETING::Target* pBackupTpm = nullptr; (void)getBackupTpm(pBackupTpm); if(pBackupTpm) { TRACUCOMP( g_trac_trustedboot, "host_update_master_tpm() - " "Backup TPM Present:%d Functional:%d Init Attempted:%d " "Usable: %d. " "Backup TPM initialization is deferred to istep 10.14.", pBackupTpm->getAttr(). present, pBackupTpm->getAttr(). functional, pBackupTpm->getAttr< TARGETING::ATTR_HB_TPM_INIT_ATTEMPTED>(), !(pPrimaryTpm->getAttr())); } TRACDCOMP( g_trac_trustedboot, EXIT_MRK"host_update_master_tpm() - %s", ((nullptr == err) ? "No Error" : "With Error") ); return err; } void tpmInitialize(TRUSTEDBOOT::TpmTarget* const i_pTpm) { assert(i_pTpm != nullptr,"tpmInitialize: BUG! i_pTpm was nullptr"); assert(i_pTpm->getAttr() == TARGETING::TYPE_TPM, "tpmInitialize: BUG! Expected target to be of TPM type, but " "it was of type 0x%08X",i_pTpm->getAttr()); errlHndl_t err = nullptr; TRACDCOMP( g_trac_trustedboot, ENTER_MRK"tpmInitialize()" ); TRACUCOMP( g_trac_trustedboot, ENTER_MRK"tpmInitialize() tgt=0x%08X", TARGETING::get_huid(i_pTpm)); do { // TPM Initialization sequence i_pTpm->setAttr(true); auto hwasState = i_pTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); hwasState.functional = true; i_pTpm->setAttr< TARGETING::ATTR_HWAS_STATE>(hwasState); // read + write bool sendStartup = true; #ifdef CONFIG_DRTM bool drtmMpipl = false; (void)SECUREBOOT::DRTM::isDrtmMpipl(drtmMpipl); if(drtmMpipl) { sendStartup = false; } #endif // Don't run STARTUP during DRTM if (sendStartup) { // TPM_STARTUP err = tpmCmdStartup(i_pTpm); if (nullptr != err) { break; } } // TPM_GETCAPABILITY to read FW Version err = tpmCmdGetCapFwVersion(i_pTpm); if (nullptr != err) { break; } #ifdef CONFIG_TPM_NVIDX_VALIDATE // Find out if in manufacturing mode TARGETING::Target* pTopLevel = nullptr; TARGETING::targetService().getTopLevelTarget(pTopLevel); assert(pTopLevel != nullptr,"Top level target was nullptr"); auto mnfgFlags = pTopLevel->getAttr(); // Only validate during MFG IPL if (mnfgFlags & TARGETING::MNFG_FLAG_SRC_TERM && !Util::isSimicsRunning()) { // TPM_GETCAPABILITY to validate NV Indexes err = tpmCmdGetCapNvIndexValidate(i_pTpm); if (nullptr != err) { break; } } #endif #ifdef CONFIG_DRTM // For a DRTM we need to reset PCRs 17-22 if (drtmMpipl) { err = tpmDrtmReset(i_pTpm); if (nullptr != err) { break; } } #endif } while ( 0 ); // If the TPM failed we will mark it not functional and commit err if (nullptr != err) { // err will be committed and set to nullptr tpmMarkFailed(i_pTpm, err); } TRACDCOMP( g_trac_trustedboot, EXIT_MRK"tpmInitialize()"); } void tpmReplayLog(TRUSTEDBOOT::TpmTarget* const i_primaryTpm, TRUSTEDBOOT::TpmTarget* const i_backupTpm) { assert(i_primaryTpm != nullptr, "tpmReplayLog: BUG! i_primaryTpm was nullptr"); assert(i_backupTpm != nullptr, "tpmReplayLog: BUG! i_backupTpm was nullptr"); assert(i_primaryTpm->getAttr() == TARGETING::TYPE_TPM, "tpmReplayLog: BUG! Expected primary target to be of TPM type, but " "it was of type 0x%08X", i_primaryTpm->getAttr()); assert(i_backupTpm->getAttr() == TARGETING::TYPE_TPM, "tpmReplayLog: BUG! Expected secondary target to be of TPM type, but" " it was of type 0x%08X", i_backupTpm->getAttr()); TRACUCOMP(g_trac_trustedboot, ENTER_MRK"tpmReplayLog()"); errlHndl_t err = nullptr; bool unMarshalError = false; // Create EVENT2 structure to be populated by getNextEvent() TCG_PCR_EVENT2 l_eventLog = {0}; // Move past header event to get a pointer to the first event // If there are no events besides the header, l_eventHndl = nullptr auto * const pTpmLogMgr = getTpmLogMgr(i_primaryTpm); auto * const bTpmLogMgr = getTpmLogMgr(i_backupTpm); assert(pTpmLogMgr != nullptr, "tpmReplayLog: BUG! Primary TPM's log manager" " is nullptr!"); assert(bTpmLogMgr != nullptr, "tpmReplayLog: BUG! Backup TPM's log manager" " is nullptr!"); const uint8_t* l_eventHndl = TpmLogMgr_getFirstEvent(pTpmLogMgr); while ( l_eventHndl != nullptr ) { // Get next event l_eventHndl = TpmLogMgr_getNextEvent(pTpmLogMgr, l_eventHndl, &l_eventLog, &unMarshalError); if (unMarshalError) { /*@ * @errortype * @reasoncode RC_TPM_UNMARSHALING_FAIL * @severity ERRL_SEV_UNRECOVERABLE * @moduleid MOD_TPM_REPLAY_LOG * @userdata1 Starting address of event that caused error * @userdata2 0 * @devdesc Unmarshal error while replaying tpm log. */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, MOD_TPM_REPLAY_LOG, RC_TPM_UNMARSHALING_FAIL, reinterpret_cast(l_eventHndl), 0, true /*Add HB SW Callout*/ ); err->collectTrace( SECURE_COMP_NAME ); err->collectTrace(TRBOOT_COMP_NAME); tpmMarkFailed(i_primaryTpm, err); break; } err = TpmLogMgr_addEvent(bTpmLogMgr, &l_eventLog); if(err) { tpmMarkFailed(i_backupTpm, err); break; } TRACUBIN(g_trac_trustedboot, "tpmReplayLog: Extending event:", &l_eventLog, sizeof(TCG_PCR_EVENT2)); for (size_t i = 0; i < l_eventLog.digests.count; i++) { TPM_Alg_Id l_algId = (TPM_Alg_Id)l_eventLog.digests.digests[i] .algorithmId; err = tpmCmdPcrExtend(i_backupTpm, (TPM_Pcr)l_eventLog.pcrIndex, l_algId, reinterpret_cast (&(l_eventLog.digests.digests[i].digest)), getDigestSize(l_algId)); if (err) { tpmMarkFailed(i_backupTpm, err); break; } } if (err) { break; } } TRACUCOMP(g_trac_trustedboot, EXIT_MRK"tpmReplayLog()"); } errlHndl_t tpmLogConfigEntries(TRUSTEDBOOT::TpmTarget* const i_pTpm) { assert(i_pTpm != nullptr,"tpmLogConfigEntries: BUG! i_pTpm was nullptr"); assert(i_pTpm->getAttr() == TARGETING::TYPE_TPM, "tpmLogConfigEntries: BUG! Expected target to be of TPM type, but " "it was of type 0x%08X",i_pTpm->getAttr()); TRACUCOMP(g_trac_trustedboot, ENTER_MRK"tpmLogConfigEntries()"); errlHndl_t l_err = nullptr; do { // Create digest buffer and set to largest config entry size. uint8_t l_digest[sizeof(uint64_t)]; memset(l_digest, 0, sizeof(uint64_t)); // Security switches uint64_t l_securitySwitchValue = 0; l_err = SECUREBOOT::getSecuritySwitch(l_securitySwitchValue, TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL); if (l_err) { break; } TRACFCOMP(g_trac_trustedboot, "security switch value = 0x%016lX", l_securitySwitchValue); // Extend to TPM - PCR_1 memcpy(l_digest, &l_securitySwitchValue, sizeof(l_securitySwitchValue)); l_err = pcrExtend(PCR_1, EV_PLATFORM_CONFIG_FLAGS, l_digest, sizeof(l_securitySwitchValue), "Security Switches"); if (l_err) { break; } memset(l_digest, 0, sizeof(uint64_t)); // Chip type and EC // Fill in the actual PVR of chip // Layout of the PVR is (32-bit): (see cpuid.C for latest format) // 2 nibbles reserved. // 2 nibbles chip type. // 1 nibble technology. // 1 nibble major DD. // 1 nibble reserved. // 1 nibble minor D uint32_t l_pvr = mmio_pvr_read() & 0xFFFFFFFF; TRACDCOMP(g_trac_trustedboot, "PVR of chip = 0x%08X", l_pvr); // Extend to TPM - PCR_1 memcpy(l_digest, &l_pvr, sizeof(l_pvr)); l_err = pcrExtend(PCR_1, EV_PLATFORM_CONFIG_FLAGS, l_digest, sizeof(l_pvr),"PVR of Chip"); if (l_err) { break; } memset(l_digest, 0, sizeof(uint64_t)); // Figure out which node we are running on TARGETING::Target* l_masterProc = nullptr; TARGETING::targetService().masterProcChipTargetHandle(l_masterProc); TARGETING::EntityPath l_entityPath = l_masterProc->getAttr(); const TARGETING::EntityPath::PathElement l_pathElement = l_entityPath.pathElementOfType(TARGETING::TYPE_NODE); uint64_t l_nodeid = l_pathElement.instance; // Extend to TPM - PCR_1,4,5,6 memcpy(l_digest, &l_nodeid, sizeof(l_nodeid)); const TPM_Pcr l_pcrs[] = {PCR_1,PCR_4,PCR_5,PCR_6}; for (size_t i = 0; i < (sizeof(l_pcrs)/sizeof(TPM_Pcr)) ; ++i) { l_err = pcrExtend(l_pcrs[i], (l_pcrs[i] == PCR_1 ? EV_PLATFORM_CONFIG_FLAGS : EV_COMPACT_HASH), l_digest, sizeof(l_nodeid),"Node id"); if (l_err) { break; } } if (l_err) { break; } // TPM Required memset(l_digest, 0, sizeof(uint64_t)); bool l_tpmRequired = isTpmRequired(); l_digest[0] = static_cast(l_tpmRequired); l_err = pcrExtend(PCR_1, EV_PLATFORM_CONFIG_FLAGS, l_digest, sizeof(l_tpmRequired), "Tpm Required"); if (l_err) { break; } // HW Key Hash SHA512_t l_hw_key_hash; SECUREBOOT::getHwKeyHash(l_hw_key_hash); l_err = pcrExtend(PCR_1, EV_PLATFORM_CONFIG_FLAGS, l_hw_key_hash, sizeof(SHA512_t),"HW KEY HASH"); if (l_err) { break; } } while(0); return l_err; } void pcrExtendSingleTpm(TpmTarget* const i_pTpm, const TPM_Pcr i_pcr, const EventTypes i_eventType, TPM_Alg_Id i_algId, const uint8_t* i_digest, size_t i_digestSize, const char* i_logMsg) { assert(i_pTpm != nullptr,"pcrExtendSingleTpm: BUG! i_pTpm was nullptr"); assert(i_pTpm->getAttr() == TARGETING::TYPE_TPM, "pcrExtendSingleTpm: BUG! Expected target to be of TPM type, but " "it was of type 0x%08X",i_pTpm->getAttr()); errlHndl_t err = nullptr; TCG_PCR_EVENT2 eventLog; bool unlock = false; TPM_Pcr pcr = i_pcr; bool useStaticLog = true; #ifdef CONFIG_DRTM // In a DRTM flow, all extensions must be re-rerouted to PCR 17 // (which will end up using locality 2). bool drtmMpipl = false; (void)SECUREBOOT::DRTM::isDrtmMpipl(drtmMpipl); if(drtmMpipl) { TRACFCOMP(g_trac_trustedboot, INFO_MRK " pcrExtendSingleTpm(): DRTM active; re-routing PCR %d " "extend to PCR 17", i_pcr); pcr = PCR_DRTM_17; useStaticLog = false; } #endif memset(&eventLog, 0, sizeof(eventLog)); do { mutex_lock( i_pTpm->getHbMutexAttr() ) ; unlock = true; auto hwasState = i_pTpm->getAttr(); // Log the event if (hwasState.present && hwasState.functional) { if (i_logMsg != nullptr) // null log indicates we don't log { // Fill in TCG_PCR_EVENT2 and add to log eventLog = TpmLogMgr_genLogEventPcrExtend(pcr, i_eventType, i_algId, i_digest, i_digestSize, TPM_ALG_SHA1, i_digest, i_digestSize, i_logMsg); if(useStaticLog) { auto * const pTpmLogMgr = getTpmLogMgr(i_pTpm); err = TpmLogMgr_addEvent(pTpmLogMgr,&eventLog); if (nullptr != err) { break; } } } // TODO: RTC 145689: Add DRTM support for using dynamic // log instead of static log; until then, inhibit DRTM logging // entirely // Perform the requested extension and also force into the // SHA1 bank err = tpmCmdPcrExtend2Hash(i_pTpm, pcr, i_algId, i_digest, i_digestSize, TPM_ALG_SHA1, i_digest, i_digestSize); } } while ( 0 ); if (nullptr != err) { // We failed to extend to this TPM we can no longer use it // Mark TPM as not functional, commit err and set it to nullptr tpmMarkFailed(i_pTpm, err); } if (unlock) { mutex_unlock( i_pTpm->getHbMutexAttr() ) ; } return; } void pcrExtendSeparator(TpmTarget* const i_pTpm) { assert(i_pTpm != nullptr,"pcrExtendSeparator: BUG! i_pTpm was nullptr"); assert(i_pTpm->getAttr() == TARGETING::TYPE_TPM, "pcrExtendSeparator: BUG! Expected target to be of TPM type, but " "it was of type 0x%08X",i_pTpm->getAttr()); errlHndl_t err = nullptr; TCG_PCR_EVENT2 eventLog = {0}; bool unlock = false; // Separators are always the same values // The digest is a sha1 hash of 0xFFFFFFFF const uint8_t sha1_digest[] = { 0xd9, 0xbe, 0x65, 0x24, 0xa5, 0xf5, 0x04, 0x7d, 0xb5, 0x86, 0x68, 0x13, 0xac, 0xf3, 0x27, 0x78, 0x92, 0xa7, 0xa3, 0x0a}; // The digest is a sha256 hash of 0xFFFFFFFF const uint8_t sha256_digest[] = { 0xAD, 0x95, 0x13, 0x1B, 0xC0, 0xB7, 0x99, 0xC0, 0xB1, 0xAF, 0x47, 0x7F, 0xB1, 0x4F, 0xCF, 0x26, 0xA6, 0xA9, 0xF7, 0x60, 0x79, 0xE4, 0x8B, 0xF0, 0x90, 0xAC, 0xB7, 0xE8, 0x36, 0x7B, 0xFD, 0x0E}; // The event message is 0xFFFFFFFF const char logMsg[] = { 0xFF, 0xFF, 0xFF, 0xFF, '\0'}; memset(&eventLog, 0, sizeof(eventLog)); do { mutex_lock( i_pTpm->getHbMutexAttr() ) ; unlock = true; std::vector pcrs = {PCR_0,PCR_1,PCR_2,PCR_3,PCR_4,PCR_5,PCR_6,PCR_7}; bool useStaticLog = true; #ifdef CONFIG_DRTM // In a DRTM flow, all extensions must be re-rerouted to PCR 17 // (which will end up using locality 2). bool drtmMpipl = false; (void)SECUREBOOT::DRTM::isDrtmMpipl(drtmMpipl); if(drtmMpipl) { TRACFCOMP(g_trac_trustedboot, INFO_MRK " pcrExtendSeparator(): DRTM active; extending " "separator to PCR 17 instead of PCR 0..7."); pcrs = { PCR_DRTM_17 }; useStaticLog = false; } #endif for (const auto &pcr : pcrs) { auto hwasState = i_pTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); // Log the separator if (hwasState.present && hwasState.functional) { // Fill in TCG_PCR_EVENT2 and add to log eventLog = TpmLogMgr_genLogEventPcrExtend(pcr, EV_SEPARATOR, TPM_ALG_SHA1, sha1_digest, sizeof(sha1_digest), TPM_ALG_SHA256, sha256_digest, sizeof(sha256_digest), logMsg); if(useStaticLog) { auto * const pTpmLogMgr = getTpmLogMgr(i_pTpm); err = TpmLogMgr_addEvent(pTpmLogMgr,&eventLog); if (nullptr != err) { break; } } // TODO: RTC 145689: Add DRTM support for using dynamic // log (which will happen any time useStaticLog is false). // Until then, we cannot log DRTM events, since they are only // allowed to go to the dynamic log // Perform the requested extension err = tpmCmdPcrExtend2Hash(i_pTpm, pcr, TPM_ALG_SHA1, sha1_digest, sizeof(sha1_digest), TPM_ALG_SHA256, sha256_digest, sizeof(sha256_digest)); if (nullptr != err) { break; } } } } while ( 0 ); if (nullptr != err) { // We failed to extend to this TPM we can no longer use it // Mark TPM as not functional, commit err and set it to nullptr tpmMarkFailed(i_pTpm, err); // Log this failure errlCommit(err, TRBOOT_COMP_ID); } if (unlock) { mutex_unlock( i_pTpm->getHbMutexAttr() ) ; } return; } void tpmMarkFailed(TpmTarget* const i_pTpm, errlHndl_t& io_err) { assert(i_pTpm != nullptr,"tpmMarkFailed: BUG! i_pTpm was nullptr"); assert(i_pTpm->getAttr() == TARGETING::TYPE_TPM, "tpmMarkFailed: BUG! Expected target to be of TPM type, but " "it was of type 0x%08X",i_pTpm->getAttr()); TRACFCOMP( g_trac_trustedboot, ENTER_MRK"tpmMarkFailed() Marking TPM as failed : " "tgt=0x%08X; io_err rc=0x%04X, plid=0x%08X", TARGETING::get_huid(i_pTpm), ERRL_GETRC_SAFE(io_err), ERRL_GETPLID_SAFE(io_err)); auto hwasState = i_pTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); hwasState.functional = false; i_pTpm->setAttr< TARGETING::ATTR_HWAS_STATE>(hwasState); // Mark the TPM as unusable so that FSP can perform alignment check i_pTpm->setAttr(true); #ifdef CONFIG_SECUREBOOT TARGETING::Target* l_tpm = i_pTpm; errlHndl_t l_err = nullptr; TARGETING::Target* l_proc = nullptr; do { if (!SECUREBOOT::enabled()) { break; } // for the given tpm target, find the processor target TARGETING::TargetHandleList l_procList; getAllChips(l_procList,TARGETING::TYPE_PROC,false); auto l_tpmInfo = l_tpm->getAttr(); for(auto it : l_procList) { auto l_physPath = it->getAttr(); if (l_tpmInfo.i2cMasterPath == l_physPath) { // found processor to deconfigure l_proc = it; break; } } if (l_proc == nullptr) { assert(false,"tpmMarkFailed - TPM with non-existent processor indicates" " a bad MRW. TPM tgt=0x%08X", TARGETING::get_huid(l_tpm)); } // set ATTR_SECUREBOOT_PROTECT_DECONFIGURED_TPM for the processor uint8_t l_protectTpm = 1; l_proc->setAttr( l_protectTpm); // do not deconfigure the processor if it already deconfigured TARGETING::PredicateHwas isNonFunctional; isNonFunctional.functional(false); if (isNonFunctional(l_proc)) { // Note: at this point l_err is nullptr so // no error log is created on break break; } uint64_t l_regValue = 0; l_err = SECUREBOOT::getSecuritySwitch(l_regValue, l_proc); if (l_err) { TRACFCOMP(g_trac_trustedboot, ERR_MRK"tpmMarkFailed - call to getSecuritySwitch failed"); break; } // if the SBE lock bit is not set, it means that istep 10.3 hasn't executed // yet, so we will let istep 10.3 call p9_update_security_control HWP // if the SBE lock bit is set, then we will call the HWP here if (!(l_regValue & static_cast(SECUREBOOT::ProcSecurity::SULBit))) { break; } const fapi2::Target l_fapiTarg(l_proc); FAPI_INVOKE_HWP(l_err, p9_update_security_ctrl, l_fapiTarg); if (l_err) { TRACFCOMP(g_trac_trustedboot, ERR_MRK"tpmMarkFailed - call to p9_update_security_ctrl failed "); } } while(0); // If we got a local error log, link it to input error log and then // commit it if (l_err) { // commit this error log first before creating the new one auto plid = l_err->plid(); // If we have an input error log then link these all together if (io_err) { TRACFCOMP(g_trac_trustedboot, ERR_MRK "tpmMarkFailed(): Processor tgt=0x%08X TPM tgt=0x%08X. " "Deconfiguring proc because future security cannot be " "guaranteed. Linking new l_err rc=0x%04X eid=0x%08X to " "io_err rc=0x%04X, plid=0x%08X", TARGETING::get_huid(l_proc), TARGETING::get_huid(l_tpm), l_err->reasonCode(), l_err->eid(), io_err->reasonCode(), io_err->plid()); // Use io_err's plid to link all errors together plid = io_err->plid(); l_err->plid(plid); } else { TRACFCOMP(g_trac_trustedboot, ERR_MRK "tpmMarkFailed(): Processor tgt=0x%08X TPM tgt=0x%08X: " "Deconfiguring proc because future security cannot be " "guaranteed due to new l_err rc=0x%04X plid=0x%08X", TARGETING::get_huid(l_proc), TARGETING::get_huid(l_tpm), l_err->reasonCode(), l_err->plid()); } ERRORLOG::ErrlUserDetailsTarget(l_proc).addToLog(l_err); l_err->collectTrace(SECURE_COMP_NAME); l_err->collectTrace(TRBOOT_COMP_NAME); // commit this error log first before creating the new one errlCommit(l_err, TRBOOT_COMP_ID); /*@ * @errortype * @reasoncode TRUSTEDBOOT::RC_UPDATE_SECURITY_CTRL_HWP_FAIL * @moduleid TRUSTEDBOOT::MOD_TPM_MARK_FAILED * @severity ERRL_SEV_UNRECOVERABLE * @userdata1 Processor Target * @userdata2 TPM Target * @devdesc Failed to set SEEPROM lock and/or TPM deconfig * protection for this processor, so we cannot * guarrantee platform secuirty for this processor * @custdesc Platform security problem detected */ l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, TRUSTEDBOOT::MOD_TPM_MARK_FAILED, TRUSTEDBOOT::RC_UPDATE_SECURITY_CTRL_HWP_FAIL, TARGETING::get_huid(l_proc), TARGETING::get_huid(l_tpm)); // Pass on the plid to connect all previous error(s) l_err->plid(plid); TRACFCOMP(g_trac_trustedboot, ERR_MRK "tpmMarkFailed(): Processor tgt=0x%08X TPM tgt=0x%08X. " "Deconfiguring proc errorlog is rc=0x%04X plid=0x%08X, eid=0x%08X", TARGETING::get_huid(l_proc), TARGETING::get_huid(l_tpm), l_err->reasonCode(), l_err->plid(), l_err->eid()); l_err->addHwCallout(l_proc, HWAS::SRCI_PRIORITY_LOW, HWAS::DELAYED_DECONFIG, HWAS::GARD_NULL); l_err->collectTrace(SECURE_COMP_NAME); l_err->collectTrace(TRBOOT_COMP_NAME); ERRORLOG::ErrlUserDetailsTarget(l_proc).addToLog(l_err); ERRORLOG::errlCommit(l_err, TRBOOT_COMP_ID); } #endif // Commit input error log if (io_err) { TRACFCOMP(g_trac_trustedboot, ERR_MRK "Committing io_err rc=0x%04X plid=0x%08X, eid=0x%08X", io_err->reasonCode(), io_err->plid(), io_err->eid()); io_err->collectTrace(SECURE_COMP_NAME); io_err->collectTrace(TRBOOT_COMP_NAME); ERRORLOG::errlCommit(io_err, TRBOOT_COMP_ID); } } void tpmVerifyFunctionalPrimaryTpmExists( const NoTpmShutdownPolicy i_noTpmShutdownPolicy) { errlHndl_t err = nullptr; bool foundFunctional = functionalPrimaryTpmExists(); const bool isBackgroundShutdown = (i_noTpmShutdownPolicy == NoTpmShutdownPolicy::BACKGROUND_SHUTDOWN); if (!foundFunctional && !systemData.failedTpmsPosted) { systemData.failedTpmsPosted = true; TRACFCOMP( g_trac_trustedboot, "NO FUNCTIONAL PRIMARY TPM FOUND ON THE NODE"); // Check to ensure jumper indicates we are running secure SECUREBOOT::SecureJumperState l_state = SECUREBOOT::SecureJumperState::SECURITY_DEASSERTED; err = SECUREBOOT::getJumperState(l_state); if (err) { auto errPlid = err->plid(); errlCommit(err, TRBOOT_COMP_ID); // we should not continue if we could not read the jumper state INITSERVICE::doShutdown(errPlid,isBackgroundShutdown); } else if (l_state == SECUREBOOT::SecureJumperState::SECURITY_ASSERTED) { if (isTpmRequired()) { /*@ * @errortype * @reasoncode RC_TPM_NOFUNCTIONALTPM_FAIL * @severity ERRL_SEV_UNRECOVERABLE * @moduleid MOD_TPM_VERIFYFUNCTIONAL * @userdata1 0 * @userdata2 0 * @devdesc The system (or node, if multi-node system) * is configured in the hardware (via processor * secure jumpers) to enable Secure Boot, and * the system's/node's "TPM required" policy is * configured to require at least one * functional boot processor TPM in order to * boot with Trusted Boot enabled. Therefore, * the system (or node, if multi-node system) * will terminate due to lack of functional * boot processor TPM. * @custdesc The system is configured for Secure Boot and * trusted platform module required mode; a * functional boot processor trusted platform * module is required to boot the system (or * node, if multi-node system), but none are * available. Therefore, the system (or node, * if multi-node system) will terminate. * Trusted platform module required mode may be * disabled via the appropriate systems * management interface to allow platform boot * without the remote trusted attestation * capability. Look for other errors which call * out the trusted platform module and follow * the repair actions for these errors. */ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, MOD_TPM_VERIFYFUNCTIONAL, RC_TPM_NOFUNCTIONALTPM_FAIL); // Add low priority HB SW callout err->addProcedureCallout(HWAS::EPUB_PRC_HB_CODE, HWAS::SRCI_PRIORITY_LOW); err->collectTrace( SECURE_COMP_NAME ); err->collectTrace(TRBOOT_COMP_NAME); err->collectTrace( I2C_COMP_NAME ); err->collectTrace( TPMDD_COMP_NAME ); uint32_t errPlid = err->plid(); // Add Security Registers to the error log SECUREBOOT::addSecurityRegistersToErrlog(err); // HW callout TPM TARGETING::Target* l_primaryTpm = nullptr; getPrimaryTpm(l_primaryTpm); if(l_primaryTpm) { err->addHwCallout(l_primaryTpm, HWAS::SRCI_PRIORITY_HIGH, HWAS::NO_DECONFIG, HWAS::GARD_NULL); } errlCommit(err, TRBOOT_COMP_ID); // terminating the IPL with this fail // Terminate IPL immediately INITSERVICE::doShutdown(errPlid,isBackgroundShutdown); } else { TRACUCOMP(g_trac_trustedboot, "No functional primary TPM found but" "TPM not Required"); } } else { TRACUCOMP(g_trac_trustedboot, "No functional primary TPM found but not running secure"); } } return; } void doInitBackupTpm() { TARGETING::Target* l_backupTpm = nullptr; errlHndl_t l_errl = nullptr; TRUSTEDBOOT::getBackupTpm(l_backupTpm); do { if(l_backupTpm) { auto l_backupHwasState = l_backupTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); // Presence-detect the secondary TPM TARGETING::TargetHandleList l_targetList; l_targetList.push_back(l_backupTpm); l_errl = HWAS::platPresenceDetect(l_targetList); if(l_errl) { errlCommit(l_errl, SECURE_COMP_ID); break; } // The TPM target would have been deleted from the list if it's // not present. if(l_targetList.size()) { l_backupHwasState.present = true; l_backupTpm->setAttr(l_backupHwasState); } else { l_backupHwasState.present = false; l_backupTpm->setAttr(l_backupHwasState); break; } mutex_lock(l_backupTpm->getHbMutexAttr()); tpmInitialize(l_backupTpm); TpmLogMgr* l_tpmLogMgr = getTpmLogMgr(l_backupTpm); if(!l_tpmLogMgr) { l_tpmLogMgr = new TpmLogMgr; setTpmLogMgr(l_backupTpm, l_tpmLogMgr); l_errl = TpmLogMgr_initialize(l_tpmLogMgr); if(l_errl) { l_backupHwasState.functional = false; l_backupTpm->setAttr (l_backupHwasState); errlCommit(l_errl, SECURE_COMP_ID); mutex_unlock(l_backupTpm-> getHbMutexAttr()); break; } } mutex_unlock(l_backupTpm-> getHbMutexAttr()); TARGETING::Target* l_primaryTpm = nullptr; getPrimaryTpm(l_primaryTpm); if(l_primaryTpm) { auto l_primaryHwasState = l_primaryTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); if(l_primaryHwasState.functional && l_primaryHwasState.present) { tpmReplayLog(l_primaryTpm, l_backupTpm); } } l_errl = TRUSTEDBOOT::testCmpPrimaryAndBackupTpm(); if(l_errl) { errlCommit(l_errl, SECURE_COMP_ID); break; } } else { TRACFCOMP(g_trac_trustedboot, "tpmDaemon: Backup TPM init message was" " received but the backup TPM cannot be found."); } } while(0); // Init was attempted even if it didn't succeed if(l_backupTpm) { l_backupTpm->setAttr(true); auto l_backupHwasState = l_backupTpm->getAttr< TARGETING::ATTR_HWAS_STATE>(); l_backupTpm->setAttr( !(l_backupHwasState.present && l_backupHwasState.functional)); } } void* tpmDaemon(void* unused) { bool shutdownPending = false; errlHndl_t err = nullptr; // Mark as an independent daemon so if it crashes we terminate task_detach(); TRACUCOMP( g_trac_trustedboot, ENTER_MRK "TpmDaemon Thread Start"); // Register shutdown events with init service. // Done at the "end" of shutdown processing. // This will flush any other messages (PCR extends) and terminate task INITSERVICE::registerShutdownEvent(systemData.msgQ, TRUSTEDBOOT::MSG_TYPE_SHUTDOWN); Message* tb_msg = nullptr; while (true) { msg_t* msg = msg_wait(systemData.msgQ); const MessageType type = static_cast(msg->type); tb_msg = nullptr; TRACUCOMP( g_trac_trustedboot, "TpmDaemon Handle CmdType %d", type); switch (type) { case TRUSTEDBOOT::MSG_TYPE_SHUTDOWN: { shutdownPending = true; // Un-register message queue from the shutdown INITSERVICE::unregisterShutdownEvent(systemData.msgQ); } break; case TRUSTEDBOOT::MSG_TYPE_PCREXTEND: { tb_msg = static_cast(msg->extra_data); TRUSTEDBOOT::PcrExtendMsgData* msgData = reinterpret_cast (tb_msg->iv_data); assert(tb_msg->iv_len == sizeof(TRUSTEDBOOT::PcrExtendMsgData) && msgData != nullptr, "Invalid PCRExtend Message"); TARGETING::TargetHandleList tpmList; // if null TPM was passed extend all TPMs. Otherwise, extend // only the TPM that was passed if (msgData->mSingleTpm == nullptr) { getTPMs(tpmList); } else { tpmList.push_back(const_cast( msgData->mSingleTpm)); } for (auto tpm : tpmList) { // Add the event to this TPM, // if an error occurs the TPM will // be marked as failed and the error log committed TRUSTEDBOOT::pcrExtendSingleTpm( tpm, msgData->mPcrIndex, msgData->mEventType, msgData->mAlgId, msgData->mDigest, msgData->mDigestSize, msgData->mMirrorToLog? msgData->mLogMsg: nullptr); } // Lastly make sure we are in a state // where we have a functional TPM TRUSTEDBOOT::tpmVerifyFunctionalPrimaryTpmExists( NoTpmShutdownPolicy::BACKGROUND_SHUTDOWN); } break; case TRUSTEDBOOT::MSG_TYPE_SEPARATOR: { tb_msg = static_cast(msg->extra_data); TARGETING::TargetHandleList tpmList; getTPMs(tpmList); for (auto tpm : tpmList) { // Add the separator to this TPM, // if an error occurs the TPM will // be marked as failed and the error log committed TRUSTEDBOOT::pcrExtendSeparator(tpm); } // Lastly make sure we are in a state // where we have a functional TPM TRUSTEDBOOT::tpmVerifyFunctionalPrimaryTpmExists( NoTpmShutdownPolicy::BACKGROUND_SHUTDOWN); } break; case TRUSTEDBOOT::MSG_TYPE_INIT_BACKUP_TPM: { doInitBackupTpm(); } break; case TRUSTEDBOOT::MSG_TYPE_GETRANDOM: { errlHndl_t err = nullptr; tb_msg = static_cast(msg->extra_data); assert(tb_msg != nullptr, "Trusted boot message pointer absent in the extra data"); tb_msg->iv_errl = nullptr; auto msgData = reinterpret_cast (tb_msg->iv_data); assert(msgData != nullptr, "Trusted boot message data pointer is null"); auto l_pTpm = msgData->i_pTpm; err = validateTpmHandle(l_pTpm); if (err) { tb_msg->iv_errl = err; err = nullptr; break; } uint8_t dataBuf[sizeof(TPM2_GetRandomOut)] = {0}; size_t dataSize = sizeof(dataBuf); auto cmd = reinterpret_cast(dataBuf); auto resp = reinterpret_cast(dataBuf); uint64_t randNum = 0; cmd->base.tag = TPM_ST_NO_SESSIONS; cmd->base.commandCode = TPM_CC_GetRandom; cmd->bytesRequested = sizeof(randNum); err = tpmTransmitCommand(l_pTpm, dataBuf, dataSize, TPM_LOCALITY_0); if (err != nullptr) { TRACFCOMP( g_trac_trustedboot, ERR_MRK"TPM GetRandom Transmit Fail! huid = 0x%08X", TARGETING::get_huid(l_pTpm)); auto l_errPlid = err->plid(); tpmMarkFailed(l_pTpm, err); /*@ * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid MOD_TPM_TPMDAEMON * @reasoncode RC_UNREACHABLE_TPM * @userdata1 TPM HUID or nullptr * @devdesc Unable to reach the TPM * @custdesc Trusted boot failure */ err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, MOD_TPM_TPMDAEMON, RC_UNREACHABLE_TPM, TARGETING::get_huid(l_pTpm), 0, true); err->plid(l_errPlid); tb_msg->iv_errl = err; err = nullptr; } else { memcpy(&randNum, resp->randomBytes.buffer,sizeof(randNum)); msgData->o_randNum = randNum; } } break; case TRUSTEDBOOT::MSG_TYPE_FLUSH: { TRACFCOMP(g_trac_trustedboot, "Flushing TPM message queue"); } break; default: assert(false, "Invalid msg command"); break; }; // Reply back, if we have a tb_msg do that way if (nullptr != tb_msg) { tb_msg->response(systemData.msgQ); } else { // use the HB message type to respond int rc = msg_respond(systemData.msgQ, msg); if (rc) { TRACFCOMP( g_trac_trustedboot, ERR_MRK "TpmDaemon: response msg_respond failure %d", rc); /*@ * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid MOD_TPM_TPMDAEMON * @reasoncode RC_MSGRESPOND_FAIL * @userdata1 rc from msq_respond() * @devdesc msg_respond() failed * @custdesc Firmware error during system boot */ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, MOD_TPM_TPMDAEMON, RC_MSGRESPOND_FAIL, rc, 0, true); err->collectTrace(SECURE_COMP_NAME); err->collectTrace(TRBOOT_COMP_NAME); // Log this failure here since we can't reply to caller errlCommit(err, TRBOOT_COMP_ID); } } if (shutdownPending) { // Exit loop and terminate task break; } } TRACUCOMP( g_trac_trustedboot, EXIT_MRK "TpmDaemon Thread Terminate"); return nullptr; } errlHndl_t validateTpmHandle(const TpmTarget* i_pTpm) { errlHndl_t err = nullptr; do { if (i_pTpm == nullptr || i_pTpm->getAttr() != TARGETING::TYPE_TPM) { TRACFCOMP(g_trac_trustedboot, ERR_MRK"Invalid TPM handle passed to GetRandom() huid = 0x%08X", TARGETING::get_huid(i_pTpm)); /*@ * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid MOD_VALIDATE_TPM_HANDLE * @reasoncode RC_INVALID_TPM_HANDLE * @userdata1 TPM HUID if it's not nullptr * @devdesc Caller attempted to get a random number from a TPM * using an invalid TPM target. * @custdesc Trusted boot failure */ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, MOD_VALIDATE_TPM_HANDLE, RC_INVALID_TPM_HANDLE, TARGETING::get_huid(i_pTpm), 0, true); break; } auto l_tpmHwasState = i_pTpm->getAttr(); if (!l_tpmHwasState.functional) { TRACFCOMP(g_trac_trustedboot, ERR_MRK"Non functional TPM handle passed to GetRandom() huid = 0x%08X", TARGETING::get_huid(i_pTpm)); /*@ * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid MOD_VALIDATE_TPM_HANDLE * @reasoncode RC_NON_FUNCTIONAL_TPM_HANDLE * @userdata1 TPM HUID if it's not nullptr * @devdesc Call attempted to get a random number from a TPM * that was not functional * @custdesc Trusted boot failure */ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, MOD_VALIDATE_TPM_HANDLE, RC_NON_FUNCTIONAL_TPM_HANDLE, TARGETING::get_huid(i_pTpm), 0, true); break; } } while(0); return err; } bool isTpmRequired() { bool retVal = false; do { // First check if sensor is available if ( getTpmRequiredSensorValue(retVal) ) { // Sensor is available so use its setting of retVal TRACUCOMP( g_trac_trustedboot, "isTpmRequired: Sensor is " "available: using retVal=%d", retVal ); break; } else { // Sensor not available; reset retVal to be safe retVal = false; } // Since sensor isn't available, use ATTR_TPM_REQUIRED TARGETING::Target* pTopLevel = nullptr; (void)TARGETING::targetService().getTopLevelTarget(pTopLevel); assert(pTopLevel != nullptr, "Unable to get top level target"); retVal = pTopLevel->getAttr(); TRACUCOMP( g_trac_trustedboot, "isTpmRequired: Using ATTR_TPM_REQUIRED:" " retVal=%d", retVal ); } while(0); TRACFCOMP( g_trac_trustedboot, "Tpm Required: %s",(retVal ? "Yes" : "No") ); return retVal; } bool getTpmRequiredSensorValue(bool& o_isTpmRequired) { bool retVal = false; o_isTpmRequired = false; // Get TPM Required Sensor #ifdef CONFIG_BMC_IPMI TARGETING::Target* pTopLevel = nullptr; (void)TARGETING::targetService().getTopLevelTarget(pTopLevel); assert(pTopLevel != nullptr, "Unable to get top level target"); uint32_t sensorNum = TARGETING::UTIL::getSensorNumber(pTopLevel, TARGETING::SENSOR_NAME_TPM_REQUIRED); TRACUCOMP( g_trac_trustedboot,"getTpmRequiredSensorValue: sensorNum=0x%X, " "enum=0x%X", sensorNum, TARGETING::SENSOR_NAME_TPM_REQUIRED); // VALID IPMI sensors are 0-0xFE if (TARGETING::UTIL::INVALID_IPMI_SENSOR != sensorNum) { // Check if TPM is required by BMC SENSOR::getSensorReadingData tpmRequiredData; SENSOR::SensorBase tpmRequired(TARGETING::SENSOR_NAME_TPM_REQUIRED, pTopLevel); errlHndl_t err = tpmRequired.readSensorData(tpmRequiredData); if (nullptr == err) { // Sensor is available and found without error retVal = true; // 0x02 == Asserted bit (TPM is required) if ((tpmRequiredData.event_status & (1 << SENSOR::ASSERTED)) == (1 << SENSOR::ASSERTED)) { o_isTpmRequired = true; } } else { // error reading sensor, so consider sensor not available TRACFCOMP( g_trac_trustedboot,ERR_MRK"getTpmRequiredSensorValue: " "Unable to read Tpm Required Sensor: rc = 0x%04X " "(sensorNum=0x%X, enum=0x%X) Deleting Error plid=0x%04X." " Considering Sensor NOT required", err->reasonCode(), sensorNum, TARGETING::SENSOR_NAME_TPM_REQUIRED, err->plid()); delete err; err = nullptr; retVal = false; } } else { // Sensor not available retVal = false; TRACUCOMP( g_trac_trustedboot, "getTpmRequiredSensorValue: Sensor " "not available: retVal=%d (sensorNum=0x%X)", retVal, sensorNum ); } #else // IPMI support not there, so consider sensor not available retVal = false; TRACUCOMP( g_trac_trustedboot, "getTpmRequiredSensorValue: IPMI Support " "not found; retVal=%d", retVal ); #endif TRACFCOMP( g_trac_trustedboot, "getTpmRequiredSensorValue: isAvail=%s, o_isTpmRequired=%s", (retVal ? "Yes" : "No"), (o_isTpmRequired ? "Yes" : "No") ); return retVal; } #ifdef CONFIG_DRTM errlHndl_t tpmDrtmReset(TpmTarget* const i_pTpm) { assert(i_pTpm != nullptr,"tpmDrtmReset: BUG! i_pTpm was nullptr"); assert(i_pTpm->getAttr() == TARGETING::TYPE_TPM, "tpmDrtmReset: BUG! Expected target to be of TPM type, but " "it was of type 0x%08X",i_pTpm->getAttr()); errlHndl_t err = nullptr; // Send to the TPM size_t len = 0; err = deviceRead(i_pTpm, nullptr, len, DEVICE_TPM_ADDRESS(TPMDD::TPM_OP_DRTMRESET, 0, TPM_LOCALITY_4)); if (nullptr == err) { /// @todo RTC: 145689 reset the dynamic tpm log } return err; } #endif #ifdef CONFIG_TPMDD errlHndl_t GetRandom(const TpmTarget* i_pTpm, uint64_t& o_randNum) { errlHndl_t err = nullptr; Message* msg = nullptr; do { auto pData = new struct GetRandomMsgData; memset(pData, 0, sizeof(*pData)); pData->i_pTpm = const_cast(i_pTpm); msg = Message::factory(MSG_TYPE_GETRANDOM, sizeof(*pData), reinterpret_cast(pData), MSG_MODE_SYNC); assert(msg != nullptr, "BUG! Message is null"); pData = nullptr; // Message owns msgData now int rc = msg_sendrecv(systemData.msgQ, msg->iv_msg); if (0 == rc) { err = msg->iv_errl; msg->iv_errl = nullptr; // taking over ownership of error log if (err != nullptr) { break; } } else // sendrecv failure { /*@ * @errortype ERRL_SEV_UNRECOVERABLE * @moduleid MOD_TPM_GETRANDOM * @reasoncode RC_SENDRECV_FAIL * @userdata1 rc from msq_sendrecv() * @userdata2 TPM HUID if it's not nullptr * @devdesc msg_sendrecv() failed * @custdesc Trusted boot failure */ err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, MOD_TPM_GETRANDOM, RC_SENDRECV_FAIL, rc, TARGETING::get_huid(i_pTpm), true); break; } pData = reinterpret_cast(msg->iv_data); assert(pData != nullptr, "BUG! Completed send/recv to random num generator has null data ptr!"); o_randNum = pData->o_randNum; } while (0); if (msg != nullptr) { delete msg; // also deletes the msg->iv_data msg = nullptr; } if (err) { err->collectTrace(SECURE_COMP_NAME); err->collectTrace(TRBOOT_COMP_NAME); } return err; } #endif // CONFIG_TPMDD errlHndl_t poisonTpm(const TpmTarget* i_pTpm) { uint64_t l_randNum = 0; errlHndl_t l_errl = nullptr; #ifdef CONFIG_TPMDD do { // Note: GetRandom validates the TPM handle internally and returns an // error log if invalid l_errl = GetRandom(i_pTpm, l_randNum); if (l_errl) { break; } const TPM_Pcr l_pcrRegs[] = {PCR_0, PCR_1, PCR_2, PCR_3, PCR_4, PCR_5, PCR_6, PCR_7}; // poison all PCR banks for (const auto l_pcrReg : l_pcrRegs) { l_errl = pcrExtend(l_pcrReg, TRUSTEDBOOT::EV_INVALID, reinterpret_cast(&l_randNum), sizeof(l_randNum), nullptr, // log not needed for poison operation false, // call synchronously to daemon i_pTpm, // only extend to pcr banks for this TPM false); // don't add PCR measurement to the log if (l_errl) { break; } } } while (0); TRACFCOMP(g_trac_trustedboot, "%ssuccessfully poisoned TPM with huid 0x%X", l_errl? "Un":"", TARGETING::get_huid(i_pTpm)); #endif return l_errl; } } // end TRUSTEDBOOT