//------------------------------------------------------------------------------ // Hardware callout ErrlUserDetailsCallout::ErrlUserDetailsCallout( const void *i_pTargetData, uint32_t i_targetDataLength, const HWAS::callOutPriority i_priority, const HWAS::DeconfigEnum i_deconfigState, const HWAS::GARD_ErrorType i_gardErrorType) { TRACDCOMP(g_trac_errl, "HWCallout entry"); // Set up ErrlUserDetails instance variables iv_CompId = ERRL_COMP_ID; iv_Version = 1; iv_SubSection = ERRL_UDT_CALLOUT; //iv_merge = false; // use the default of false uint32_t pDataLength = sizeof(HWAS::callout_ud_t) + i_targetDataLength; HWAS::callout_ud_t *pData; pData = reinterpret_cast<HWAS::callout_ud_t *> (reallocUsrBuf(pDataLength)); pData->type = HWAS::HW_CALLOUT; pData->priority = i_priority; #ifndef __HOSTBOOT_RUNTIME pData->cpuid = task_getcpuid(); #else pData->cpuid = (uint32_t)(-1); #endif pData->deconfigState = i_deconfigState; pData->gardErrorType = i_gardErrorType; memcpy(pData + 1, i_pTargetData, i_targetDataLength); TRACDCOMP(g_trac_errl, "HWCallout exit; pDataLength %d", pDataLength); } // Hardware callout
/** * @brief Get the Virtual Address of the XSCOM space for the processor * associated with this thread (the source chip) * * @return uint64_t* virtualAddress */ uint64_t* getCpuIdVirtualAddress() { uint64_t* o_virtAddr = 0; // Get the CPU core this thread is running on uint32_t cpuid = task_getcpuid(); //NNNCCCPPPPTTT format fot the cpuid.. // N = node, C = chip, P = proc, T = thread uint32_t chipId = (cpuid & 0x0380)>>7; uint32_t nodeId = (cpuid & 0x1C00)>>10; // Can change the above hardcoded values to either a macro or use // the info below to do the masking and shifting. // uint64_t max_threads = cpu_thread_count(); // for the number of Chips - use g_xscomMaxChipsPerNode instead.. // For the number of Procs.. MAX_PROCS_RSV = P8_MAX_PROCS*2 // P8_MAX_PROCS = 8 -- space left for 2* that. XSComBase_t l_systemBaseAddr = MASTER_PROC_XSCOM_BASE_ADDR; // Target's XSCOM Base address XSComBase_t l_XSComBaseAddr = l_systemBaseAddr + ( ( (g_xscomMaxChipsPerNode * nodeId) + chipId ) * THIRTYTWO_GB); // Target's virtual address o_virtAddr = static_cast<uint64_t*> (mmio_dev_map(reinterpret_cast<void*>(l_XSComBaseAddr), THIRTYTWO_GB)); TRACDCOMP(g_trac_xscom, "getCpuIdVirtualAddress: o_Virtual Address = 0x%llX\n",o_virtAddr); return o_virtAddr; }
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; }
/** * @brief Get the virtual address of the input target * for an XSCOM access. * * Logic: * * If sentinel: * If never XSCOM to sentinel * Calculate virtual addr for sentinel * Save it to g_masterProcVirtAddr for future XSCOM to sentinel * Else * Use virtual addr stored in g_masterProcVirtAddr * End if * Else (not sentinel) * If never XSCOM to this chip: * If this is a master processor object * Use virtual addr stored for sentinel (g_masterProcVirtAddr) * Else * Call mmio_dev_map() to get virtual addr for this slave proc * End if * Save virtual addr used to this chip's attribute * Else * Use virtual address stored in this chip's attributes. * End if * End if * * @param[in] i_target XSCom target * @param[out] o_virtAddr Target's virtual address * * @return errlHndl_t */ errlHndl_t getTargetVirtualAddress(TARGETING::Target* i_target, uint64_t*& o_virtAddr) { errlHndl_t l_err = NULL; o_virtAddr = NULL; XSComBase_t l_XSComBaseAddr = 0; do { // Find out if the target pointer is the master processor chip bool l_isMasterProcChip = false; if (i_target == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL) { // Sentinel pointer representing the master processor chip l_isMasterProcChip = true; } else { TARGETING::Target* l_pMasterProcChip = NULL; TARGETING::targetService(). masterProcChipTargetHandle(l_pMasterProcChip); if (i_target == l_pMasterProcChip) { // Target Service reports that this is the master processor chip l_isMasterProcChip = true; } } // If the target is the master processor chip sentinel if (l_isMasterProcChip) { // This is the master processor chip. The virtual address is // g_masterProcVirtAddr. If this is NULL then initialize it // Use atomic update instructions here to avoid // race condition between different threads. // Keep in mind that the mutex used in XSCOM is hardware mutex, // not a mutex for the whole XSCOM logic. if (__sync_bool_compare_and_swap(&g_masterProcVirtAddr, NULL, NULL)) { // Note: can't call TARGETING code prior to PNOR being // brought up. uint64_t l_mmioAddr = 0; uint64_t* l_tempVirtAddr = getCpuIdVirtualAddress(l_mmioAddr); if (!__sync_bool_compare_and_swap(&g_masterProcVirtAddr, NULL, l_tempVirtAddr)) { // If g_masterProcVirtAddr has already been updated by // another thread, we need to unmap the dev_map we just // called above. int rc = 0; rc = mmio_dev_unmap(reinterpret_cast<void*> (l_tempVirtAddr)); if (rc != 0) { /*@ * @errortype * @moduleid XSCOM_GET_TARGET_VIRT_ADDR * @reasoncode XSCOM_MMIO_UNMAP_ERR * @userdata1 Return Code * @userdata2 Unmap address * @devdesc mmio_dev_unmap() returns error * @custdesc A problem occurred during the IPL * of the system. */ l_err = new ERRORLOG::ErrlEntry( ERRORLOG::ERRL_SEV_UNRECOVERABLE, XSCOM_GET_TARGET_VIRT_ADDR, XSCOM_MMIO_UNMAP_ERR, rc, reinterpret_cast<uint64_t>(l_tempVirtAddr), true /*Add HB Software Callout*/); break; } } else { TRACFCOMP(g_trac_xscom, "Master Proc : pir=%.8X, mmio=0x%0.16llX, virt=0x%llX", task_getcpuid(), l_mmioAddr, reinterpret_cast<uint64_t>(l_tempVirtAddr)); } } // Set virtual address to sentinel's value o_virtAddr = g_masterProcVirtAddr; } else // This is not the master sentinel { // Get the virtual addr value of the chip from the virtual address // attribute o_virtAddr = reinterpret_cast<uint64_t*>( i_target->getAttr<TARGETING::ATTR_XSCOM_VIRTUAL_ADDR>()); // If the virtual address equals NULL(default) then this is the // first XSCOM to this target so we need to map in the appropriate // address if (o_virtAddr == NULL) { uint64_t xscomGroupId = 0; uint64_t xscomChipId = 0; // Get the target Group Id xscomGroupId = i_target->getAttr<TARGETING::ATTR_FABRIC_GROUP_ID>(); // Get the target Chip Id xscomChipId = i_target->getAttr<TARGETING::ATTR_FABRIC_CHIP_ID>(); // Get assigned XSCOM base address l_XSComBaseAddr = i_target->getAttr<TARGETING::ATTR_XSCOM_BASE_ADDRESS>(); TRACFCOMP(g_trac_xscom, "Target %.8X :: Group:%d Chip:%d :: XscomBase:0x%llX", TARGETING::get_huid(i_target), xscomGroupId, xscomChipId, l_XSComBaseAddr); // Target's virtual address o_virtAddr = static_cast<uint64_t*> (mmio_dev_map(reinterpret_cast<void*>(l_XSComBaseAddr), THIRTYTWO_GB)); TRACDCOMP(g_trac_xscom, "xscomPerformOp: o_Virtual Address = 0x%llX\n",o_virtAddr); // Implemented the virtual address attribute.. // Leaving the comments as a discussion point... // Technically there is a race condition here. The mutex is // a per-hardware thread mutex, not a mutex for the whole XSCOM // logic. So there is possibility that this same thread is running // on another thread at the exact same time. We can use atomic // update instructions here. // Future note : This is a good candidate for having a way // to return a reference to the attribute instead of requiring // to call setAttr. We currently have no way to SMP-safely update // this attribute, where as if we had a reference to it we could use // the atomic update functions (_sync_bool_compare_and_swap in // this case. // Save the virtual address attribute. i_target->setAttr<TARGETING::ATTR_XSCOM_VIRTUAL_ADDR>( reinterpret_cast<uint64_t>(o_virtAddr)); } } } while (0); return l_err; }