diff options
Diffstat (limited to 'src/usr/secureboot/ext/drtm.C')
-rw-r--r-- | src/usr/secureboot/ext/drtm.C | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/src/usr/secureboot/ext/drtm.C b/src/usr/secureboot/ext/drtm.C new file mode 100644 index 000000000..1497e35e3 --- /dev/null +++ b/src/usr/secureboot/ext/drtm.C @@ -0,0 +1,723 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/usr/secureboot/ext/drtm.C $ */ +/* */ +/* OpenPOWER HostBoot Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2013,2017 */ +/* [+] 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 */ + +#include <stdint.h> +#include <config.h> +#include <builtins.h> +#include <limits.h> +#include <string.h> +#include <vector> +#include <algorithm> + +#include <sys/mm.h> +#include <sys/task.h> +#include <util/align.H> +#include <errl/errlentry.H> +#include <errl/errlmanager.H> +#include <targeting/common/util.H> +#include <targeting/common/utilFilter.H> +#include <targeting/common/commontargeting.H> +#include <arch/pirformat.H> +#include <initservice/mboxRegs.H> +#include <util/utilmbox_scratch.H> +#include <secureboot/settings.H> +#include <secureboot/service.H> +#include <secureboot/secure_reasoncodes.H> +#include <secureboot/trustedbootif.H> +#include <secureboot/drtm.H> + +#include "../common/securetrace.H" + +// Set to "1" to enable unit tracing +#if 0 + // Enable SB_UNIT + #define TRACUCOMP(args...) TRACFCOMP(args) + // Enable SB_UNIT_BIN + #define TRACUBIN(args...) TRACFBIN(args) +#else + #define TRACUCOMP(args...) + #define TRACUBIN(args...) +#endif + +// Makes the math more intuitive +#define BYTES_PER_MEGABYTE MEGABYTE + +namespace SECUREBOOT +{ + +namespace DRTM +{ + +#ifdef CONFIG_DRTM_TRIGGERING + +// RIT protection DRTM payload address in megabytes +// Use reserved area immediately before payload base +const uint32_t DRTM_RIT_PAYLOAD_PHYS_ADDR_MB = 256-1; + +// RIT protection payload +const char DRTM_RIT_PAYLOAD[] = {'D','R','T','M'}; + +const char* const DRTM_RIT_LOG_TEXT = "DrtmPayload"; + +#endif + +errlHndl_t discoverDrtmState( + const INITSERVICE::SPLESS::MboxScratch7_t& i_scratchReg7, + const INITSERVICE::SPLESS::MboxScratch8_t& i_scratchReg8) +{ + SB_ENTER("discoverDrtmState: i_scratchReg7=0x%08X, " + "i_scratchReg8 = 0x%08X", + i_scratchReg7.data32,i_scratchReg8.data32); + + errlHndl_t pError = nullptr; + + do + { + + TARGETING::Target* pSysTarget = nullptr; + TARGETING::targetService().getTopLevelTarget(pSysTarget); + + if(pSysTarget == nullptr) + { + // TODO: RTC 167205: GA error handling + assert(false,"discoverDrtmState: BUG! nullptr system target detected."); + break; + } + + TARGETING::Target* pMasterProc = nullptr; + pError = TARGETING::targetService().queryMasterProcChipTargetHandle( + pMasterProc); + if(pError) + { + SB_ERR("discoverDrtmState: Failed in call to " + "queryMasterProcChipTargetHandle()."); + break; + } + + uint64_t securitySwitches = 0; + pError = SECUREBOOT::getSecuritySwitch(securitySwitches, + pMasterProc); + if(pError) + { + SB_ERR("discoverDrtmState: Failed in call to getSecuritySwitch() for " + "proc = 0x%08X.", + get_huid(pMasterProc)); + break; + } + + const bool masterProcL4A = securitySwitches + & static_cast<uint64_t>(SECUREBOOT::ProcSecurity::L4ABit); + const bool masterProcLQA = securitySwitches + & static_cast<uint64_t>(SECUREBOOT::ProcSecurity::LQABit); + const bool masterProcLLP = securitySwitches + & static_cast<uint64_t>(SECUREBOOT::ProcSecurity::LLPBit); + + const bool drtmPayloadValid = i_scratchReg8.validDrtmPayloadAddr; + const bool isMpiplBoot = pSysTarget->getAttr<TARGETING::ATTR_IS_MPIPL_HB>(); + const bool securebootEnabled = SECUREBOOT::enabled(); + + SB_INF("discoverDrtmState: masterProcL4A = %d, drtmPayloadValid = %d, " + "isMpiplBoot = %d, securebootEnabled = %d, " + "masterProcLQA = %d, i_scratchReg7 = 0x%08X, " + "masterProcLLP = %d.", + masterProcL4A,drtmPayloadValid,isMpiplBoot, + securebootEnabled,masterProcLQA,i_scratchReg7.drtmPayloadAddrMb, + masterProcLLP); + + TARGETING::ATTR_IS_DRTM_MPIPL_HB_type drtmMpIpl = false; + if(masterProcL4A || drtmPayloadValid) + { + // Note: We don't care if trusted boot is not enabled, if not, the + // measurement will simply be ignored later + if( !masterProcL4A + || !drtmPayloadValid + || !isMpiplBoot + || !securebootEnabled + || !masterProcLQA + || masterProcLLP) + { + // TODO: RTC 167205: GA error handling + assert(false,"discoverDrtmState: BUG! Inconsistent DRTM state " + "detected. masterProcL4A = %d, drtmPayloadValid = %d, " + "isMpiplBoot = %d, securebootEnabled = %d, " + "masterProcLQA = %d, masterProcLLP = %d.", + masterProcL4A,drtmPayloadValid,isMpiplBoot, + securebootEnabled,masterProcLQA,masterProcLLP); + break; + } + else + { + drtmMpIpl = true; + } + } + + if( drtmMpIpl + && (i_scratchReg7.drtmPayloadAddrMb == 0)) + { + // TODO: RTC 167205: GA error handling + assert(false,"discoverDrtmState: BUG! DRTM payload address " + "should not be 0 on a DRTM boot."); + break; + } + + const auto drtmPayloadAddrMb = drtmMpIpl ? + i_scratchReg7.drtmPayloadAddrMb : 0; + + pSysTarget->setAttr<TARGETING::ATTR_IS_DRTM_MPIPL_HB>(drtmMpIpl); + pSysTarget->setAttr<TARGETING::ATTR_DRTM_PAYLOAD_ADDR_MB_HB>( + drtmPayloadAddrMb); + + // NOTE: It should be SBE job to clear the DRTM scratch regs + // on any given non-DRTM boot flow, hence Hostboot never has to clear them. + + } while(0); + + if(pError) + { + SB_ERR("discoverDrtmState: plid=0x%08X, eid=0x%08X, reason=0x%04X", + ERRL_GETPLID_SAFE(pError), + ERRL_GETEID_SAFE(pError), + ERRL_GETRC_SAFE(pError)); + } + + SB_EXIT("discoverDrtmState"); + + return pError; +} + +void isDrtmMpipl(bool& o_isDrtmMpipl) +{ + TARGETING::Target* pSysTarget = nullptr; + TARGETING::targetService().getTopLevelTarget(pSysTarget); + + if(pSysTarget == nullptr) + { + // TODO: RTC 167205: GA error handling + assert(false,"isDrtmMpipl: BUG! nullptr system target detected"); + } + + o_isDrtmMpipl = pSysTarget->getAttr<TARGETING::ATTR_IS_DRTM_MPIPL_HB>(); +} + +errlHndl_t validateDrtmHwSignature() +{ + SB_ENTER("validateDrtmHwSignature"); + + errlHndl_t pError = nullptr; + + do + { + bool drtmMpIpl = false; + isDrtmMpipl(drtmMpIpl); + + if(drtmMpIpl) + { + SB_DBG("validateDrtmHwSignature: DRTM active, checking L4A, LQA, " + "SUL, LLS and LLP on node's functional proc chips."); + + TARGETING::TargetHandleList funcProcChips; + TARGETING::getAllChips(funcProcChips, + TARGETING::TYPE_PROC); + for(auto &pFuncProc :funcProcChips) + { + uint64_t securitySwitches = 0; + pError = SECUREBOOT::getSecuritySwitch(securitySwitches, + pFuncProc); + if(pError) + { + SB_ERR("validateDrtmHwSignature: getSecuritySwitch() " + "failed for proc = 0x%08X.", + get_huid(pFuncProc)); + break; + } + + const bool L4A = securitySwitches + & static_cast<uint64_t>(SECUREBOOT::ProcSecurity::L4ABit); + const bool LQA = securitySwitches + & static_cast<uint64_t>(SECUREBOOT::ProcSecurity::LQABit); + const bool SUL = securitySwitches + & static_cast<uint64_t>(SECUREBOOT::ProcSecurity::SULBit); + const bool LLP = securitySwitches + & static_cast<uint64_t>(SECUREBOOT::ProcSecurity::LLPBit); + const bool LLS = securitySwitches + & static_cast<uint64_t>(SECUREBOOT::ProcSecurity::LLSBit); + + SB_INF("validateDrtmHwSignature: Proc 0x%08X has L4A = %d, " + "LQA = %d, SUL = %d, LLP = %d, LLS = %d.", + get_huid(pFuncProc),L4A,LQA,SUL,LLP,LLS); + + if(!L4A || !LQA || !SUL || LLP || LLS) + { + // TODO: RTC 167205: GA error handling, including whether + // to attempt on every processor + assert(false,"validateDrtmHwSignature: BUG! In DRTM flow, " + "all functional proc chips should have L4A, LQA, and " + "SUL set + LLP and LLS clear, however proc 0x%08X has " + "L4A = %d, LQA = %d, SUL = %d, LLP = %d, LLS = %d.", + get_huid(pFuncProc),L4A,LQA,SUL,LLP,LLS); + break; + } + } + + if(pError) + { + break; + } + } + else + { + SB_INF("validateDrtmHwSignature: DRTM not active, skipping check " + "for L4A, LQA, SUL, LLP and LLS on node's functional procs"); + } + + } while(0); + + if(pError) + { + SB_ERR("validateDrtmHwSignature: plid=0x%08X, eid=0x%08X, " + "reason=0x%04X", + ERRL_GETPLID_SAFE(pError), + ERRL_GETEID_SAFE(pError), + ERRL_GETRC_SAFE(pError)); + } + + SB_EXIT("validateDrtmHwSignature"); + + return pError; +} + +errlHndl_t validateDrtmPayload() +{ + SB_ENTER("validateDrtmPayload"); + + errlHndl_t pError = nullptr; + const void* drtmPayloadVirtAddr = nullptr; + + do + { + bool drtmMpIpl = false; + isDrtmMpipl(drtmMpIpl); + + if(drtmMpIpl) + { + SB_DBG("validateDrtmPayload: DRTM active, validating DRTM payload." + "proc chips."); + + TARGETING::Target* pSysTarget = nullptr; + TARGETING::targetService().getTopLevelTarget(pSysTarget); + + if(pSysTarget == nullptr) + { + // TODO: RTC 167205: GA error handling + assert(false,"validateDrtmPayload: BUG! nullptr system target " + "detected"); + break; + } + + const auto drtmPayloadPhysAddrMb = pSysTarget->getAttr< + TARGETING::ATTR_DRTM_PAYLOAD_ADDR_MB_HB>(); + + if(drtmPayloadPhysAddrMb == 0) + { + assert(false,"validateDrtmPayload: BUG! DRTM payload physical " + "address should not be 0."); + break; + } + + const uint64_t drtmPayloadPhysAddr = + drtmPayloadPhysAddrMb*BYTES_PER_MEGABYTE; + + SB_INF("validateDrtmPayload: DRTM payload available at physical " + "address of %d MB (0x%16llX).", + drtmPayloadPhysAddrMb,drtmPayloadPhysAddr); + + // Compute DRTM payload size + // TODO: RTC 167205: Once the DRTM save area is known/defined, + // need to figure out a better initial size to map. For example, + // perhaps we map just one page to begin with, in order to read out + // the actual total size. Also, a size is available, assert if the + // size is 0. + uint64_t drtmPayloadSize = 0; + #ifdef CONFIG_DRTM_TRIGGERING + drtmPayloadSize = ALIGN_PAGE(sizeof(DRTM_RIT_PAYLOAD)); + #endif + + // Map in the physical memory to virtual memory + drtmPayloadVirtAddr = mm_block_map ( + reinterpret_cast<void*>(drtmPayloadPhysAddr),drtmPayloadSize); + if(drtmPayloadVirtAddr == nullptr) + { + // TODO: RTC 167205: GA error handling + assert(false,"validateDrtmPayload: BUG! mm_block_map returned " + "nullptr for physical address 0x%016llX and size " + "0x%016llX.", + drtmPayloadPhysAddr,drtmPayloadSize); + break; + } + + #ifdef CONFIG_DRTM_TRIGGERING + + // Verify the payload matches expected result + if(memcmp(drtmPayloadVirtAddr,DRTM_RIT_PAYLOAD, + sizeof(DRTM_RIT_PAYLOAD) != 0)) + { + const uint32_t* pAddrAct = reinterpret_cast<const uint32_t*>( + drtmPayloadVirtAddr); + const uint32_t* pAddrExp = reinterpret_cast<const uint32_t*>( + &DRTM_RIT_PAYLOAD); + + SB_ERR("validateDrtmPayload: DRTM RIT: payload content at " + "0x%16llX was not as expected. Expected value = 0x%08X, " + "actual = 0x%08X", + drtmPayloadVirtAddr, + *pAddrAct, + *pAddrExp); + + // TODO: RTC 167205: GA error handling + assert(false,"validateDrtmPayload: BUG: DRTM payload content " + "at 0x%16llX was not as expected. Expected value = " + "0x%08X, actual = 0x%08X", + drtmPayloadVirtAddr, + *pAddrAct, + *pAddrExp); + break; + } + + // Extend (arbitrary) measurement to PCR17 + SHA512_t hash = {0}; + memcpy(hash,DRTM_RIT_PAYLOAD,sizeof(DRTM_RIT_PAYLOAD)); + pError = TRUSTEDBOOT::pcrExtend(TRUSTEDBOOT::PCR_DRTM_17, hash, + sizeof(SHA512_t),DRTM_RIT_LOG_TEXT); + if(pError) + { + SB_ERR("validateDrtmPayload: Failed in pcrExtend() for PCR 17"); + break; + } + + #else + + // TODO: RTC 167205: Securely verify the measured launch environment + // TODO: RTC 167205: Really measure+extend the payload + + #endif + } + else + { + SB_INF("validateDrtmPayload: DRTM not active, skipping DRTM " + "payload verification "); + } + + } while(0); + + if(drtmPayloadVirtAddr) + { + auto rc = mm_block_unmap(const_cast<void*>(drtmPayloadVirtAddr)); + if(rc != 0) + { + // TODO: RTC 167205: GA error handling + assert(false,"validateDrtmPayload: BUG! mm_block_unmap failed for " + "virtual address 0x%16llX.", + drtmPayloadVirtAddr); + + } + } + + if(pError) + { + SB_ERR("validateDrtmPayload: plid=0x%08X, eid=0x%08X, reason=0x%04X", + ERRL_GETPLID_SAFE(pError), + ERRL_GETEID_SAFE(pError), + ERRL_GETRC_SAFE(pError)); + } + + SB_EXIT("validateDrtmPayload"); + + return pError; +} + +errlHndl_t completeDrtm() +{ + SB_ENTER("completeDrtm"); + + errlHndl_t pError = nullptr; + + do + { + bool drtmMpIpl = false; + isDrtmMpipl(drtmMpIpl); + + if(drtmMpIpl) + { + SB_INF("completeDrtm: Clearing L4A and LQA on node's functional " + "proc chips."); + + const std::vector<SECUREBOOT::ProcSecurity> bitsToClear { + SECUREBOOT::ProcSecurity::L4ABit, + SECUREBOOT::ProcSecurity::LQABit + }; + + TARGETING::TargetHandleList funcProcChips; + TARGETING::getAllChips(funcProcChips, + TARGETING::TYPE_PROC); + + for(auto &pFuncProc :funcProcChips) + { + pError = SECUREBOOT::clearSecuritySwitchBits(bitsToClear, + pFuncProc); + if(pError) + { + // TODO: RTC 167205: GA error handling to attempt on every + // processor + SB_ERR("completeDrtm: clearSecuritySwitchBits() failed for " + "proc = 0x%08X. Tried to clear LQA + L4A.", + get_huid(pFuncProc)); + break; + } + } + + if(pError) + { + break; + } + + } + else + { + SB_INF("completeDrtm: DRTM not active, not clearing LQA or L4A " + "bits."); + } + + } while(0); + + if(pError) + { + SB_ERR("completeDrtm: plid=0x%08X, eid=0x%08X, reason=0x%04X", + ERRL_GETPLID_SAFE(pError), + ERRL_GETEID_SAFE(pError), + ERRL_GETRC_SAFE(pError)); + } + + SB_EXIT("completeDrtm"); + + return pError; +} + +#ifdef CONFIG_DRTM_TRIGGERING + +errlHndl_t initiateDrtm() +{ + SB_ENTER("initiateDrtm"); + + errlHndl_t pError = nullptr; + + // For DRTM, the thread has to be pinned to a core (and therefore pinned to + // a chip) + task_affinity_pin(); + + void* drtmPayloadVirtAddr = nullptr; + + do + { + const std::vector<SECUREBOOT::ProcSecurity> LLP { + SECUREBOOT::ProcSecurity::LLPBit, + }; + + const std::vector<SECUREBOOT::ProcSecurity> LLS { + SECUREBOOT::ProcSecurity::LLSBit, + }; + + // Determine which fabric group and chip this task is executing on and + // create a filter to find the matching chip target + auto cpuId = task_getcpuid(); + auto groupId = PIR_t::groupFromPir(cpuId); + auto chipId = PIR_t::chipFromPir(cpuId); + TARGETING::PredicateAttrVal<TARGETING::ATTR_FABRIC_GROUP_ID> + matchesGroup(groupId); + TARGETING::PredicateAttrVal<TARGETING::ATTR_FABRIC_CHIP_ID> + matchesChip(chipId); + TARGETING::PredicatePostfixExpr matchesGroupAndChip; + matchesGroupAndChip.push(&matchesGroup).push(&matchesChip).And(); + + // Get all the functional proc chips and find the chip we're running on + TARGETING::TargetHandleList funcProcChips; + TARGETING::getAllChips(funcProcChips, + TARGETING::TYPE_PROC); + if(funcProcChips.empty()) + { + // TODO: RTC 167205: GA error handling + assert(false,"initiateDrtm: BUG! Functional proc chips is empty, " + "yet this code is running on a functional chip!"); + break; + } + + // NOTE: std::find_if requires predicates to be copy constructable, but + // predicates are not; hence use a wrapper lambda function to bypass + // that limitation + auto pMatch = + std::find_if(funcProcChips.begin(),funcProcChips.end(), + [&matchesGroupAndChip] ( TARGETING::Target* pTarget ) + { + return matchesGroupAndChip(pTarget); + } ); + + if(pMatch == funcProcChips.end()) + { + // TODO: RTC 167205: GA error handling + assert(false, "initiateDrtm: BUG! No functional chip found " + "to be running this code"); + break; + } + + // Move the matching target to the end of the list. + // NOTE: If reverse iterators were supported, we could have verified the + // last element of the container is not the match, and done a + // std::iter_swap of the match and the last element + TARGETING::Target* const pMatchTarget = *pMatch; + funcProcChips.erase(pMatch); + funcProcChips.push_back(pMatchTarget); + + // Map to the DRTM payload area in mainstore + const uint32_t drtmPayloadPhysAddrMb = DRTM_RIT_PAYLOAD_PHYS_ADDR_MB; + drtmPayloadVirtAddr = mm_block_map( + reinterpret_cast<void*>(drtmPayloadPhysAddrMb*BYTES_PER_MEGABYTE), + PAGESIZE); + if(drtmPayloadVirtAddr == nullptr) + { + // TODO: RTC 167205: GA error handling + assert(false, "initiateDrtm: BUG! Failed in call to mm_block_map " + "to map the DRTM payload."); + break; + } + + // Copy the DRTM payload to the DRTM payload area + memcpy( + reinterpret_cast<uint32_t*>(drtmPayloadVirtAddr), + DRTM_RIT_PAYLOAD, + sizeof(DRTM_RIT_PAYLOAD)); + + // The required generic sequencing to initiate DRTM is as follows: + // 1) Initiating task must pin itself to a core (to ensure it + // will not be accidentally queisced by SBE) + // 2) It must set the DRTM payload information in the master processor + // mailbox scratch registers (registers 7 and 8) before it goes + // offline + // 3) It must determine the processor it's currently running on + // 4) It must set the late launch bit (LL) on all other processors + // 4a) If the given processor is an active master, it must set + // late launch primary (LLP) bit + // 4b) Otherwise it must set late launch secondary (LLS) bit + // 5) Finally, it must its own processor's LL bit last, according to the + // rules of step 4. + for(auto &pFuncProc :funcProcChips) + { + const auto procMasterType = pFuncProc->getAttr< + TARGETING::ATTR_PROC_MASTER_TYPE>(); + + // If master chip, set the DRTM payload address and validity + if(procMasterType == TARGETING::PROC_MASTER_TYPE_ACTING_MASTER) + { + (void)setDrtmPayloadPhysAddrMb(drtmPayloadPhysAddrMb); + } + + pError = SECUREBOOT::setSecuritySwitchBits(procMasterType == + TARGETING::PROC_MASTER_TYPE_ACTING_MASTER ? + LLP : LLS, + pFuncProc); + if(pError) + { + SB_ERR("initiateDrtm: setSecuritySwitchBits() failed for proc " + "= 0x%08X. Tried to set LLP or LLS.", + get_huid(pFuncProc)); + break; + } + } + + if(pError) + { + break; + } + + + SB_INF("initiateDrtm: SBE should eventually quiesce all cores; until " + "then, endlessly yield the task"); + while(1) + { + task_yield(); + } + + } while(0); + + // If we -do- come back from this function (on error path only), then we + // should unpin + task_affinity_unpin(); + + if(drtmPayloadVirtAddr) + { + auto rc = mm_block_unmap(const_cast<void*>(drtmPayloadVirtAddr)); + if(rc != 0) + { + // TODO: RTC 167205: GA error handling + assert(false,"initiateDrtm: BUG! mm_block_unmap failed for virtual " + "address 0x%16llX.", + drtmPayloadVirtAddr); + } + } + + if(pError) + { + SB_ERR("initiateDrtm: plid=0x%08X, eid=0x%08X, reason=0x%04X", + ERRL_GETPLID_SAFE(pError), + ERRL_GETEID_SAFE(pError), + ERRL_GETRC_SAFE(pError)); + } + + SB_EXIT("initiateDrtm"); + + return pError; +} + +void setDrtmPayloadPhysAddrMb( + const uint32_t i_drtmPayloadPhysAddrMb) +{ + // Set the address + Util::writeScratchReg( + INITSERVICE::SPLESS::MBOX_SCRATCH_REG7,i_drtmPayloadPhysAddrMb); + + // Mark as valid if non-0, else invalid + INITSERVICE::SPLESS::MboxScratch8_t scratch8 = + { .data32 = Util::readScratchReg( + INITSERVICE::SPLESS::MBOX_SCRATCH_REG8) }; + scratch8.validDrtmPayloadAddr = + i_drtmPayloadPhysAddrMb ? true : false; + Util::writeScratchReg( + INITSERVICE::SPLESS::MBOX_SCRATCH_REG8,scratch8.data32); +} + +#endif // CONFIG_DRTM_TRIGGERING + +} // End DRTM namespace + +} // End SECUREBOOT namespace + |