blob: 5c0f2e5e56416aa548f529854387c2282dcce5c8 [file] [log] [blame]
/* IBM_PROLOG_BEGIN_TAG */
/* This is an automatically generated prolog. */
/* */
/* $Source: src/import/chips/p9/procedures/utils/stopreg/p9_stop_api.C $ */
/* */
/* OpenPOWER HostBoot Project */
/* */
/* Contributors Listed Below - COPYRIGHT 2015,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 */
///
/// @file p9_stop_api.C
/// @brief implements STOP API which create/manipulate STOP image.
///
// *HWP HW Owner : Greg Still <stillgs@us.ibm.com>
// *HWP FW Owner : Prem Shanker Jha <premjha2@in.ibm.com>
// *HWP Team : PM
// *HWP Level : 2
// *HWP Consumed by : HB:HYP
#include "p9_stop_api.H"
#include "p9_cpu_reg_restore_instruction.H"
#include "p9_stop_data_struct.H"
#include <string.h>
#include "p9_stop_util.H"
#include <stdio.h>
#ifdef __FAPI_2_
#include <fapi2.H>
#endif
#ifdef __cplusplus
extern "C" {
namespace stopImageSection
{
#endif
// a true in the table below means register is of scope thread
// whereas a false meanse register is of scope core.
const StopSprReg_t g_sprRegister[] =
{
{ P9_STOP_SPR_HSPRG0, true },
{ P9_STOP_SPR_HRMOR, false },
{ P9_STOP_SPR_LPCR, true },
{ P9_STOP_SPR_HMEER, false },
{ P9_STOP_SPR_LDBAR, true },
{ P9_STOP_SPR_PSSCR, true },
{ P9_STOP_SPR_PMCR, false },
{ P9_STOP_SPR_HID, false },
{ P9_STOP_SPR_MSR, true },
{ P9_STOP_SPR_DAWR, true },
};
const uint32_t MAX_SPR_SUPPORTED =
sizeof ( g_sprRegister ) / sizeof( StopSprReg_t );
//-----------------------------------------------------------------------------
/**
* @brief validates input arguments provided by STOP API caller.
* @param[in] i_pImage pointer to beginning of chip's HOMER image.
* @param[in] i_regId SPR register id
* @param[in] i_coreId core id
* @param[in|out] i_pThreadId points to thread id
* @param[in|out] i_pThreadLevelReg points to scope information of SPR
* @return STOP_SAVE_SUCCESS if arguments found valid, error code otherwise.
* @note for register of scope core, function shall force io_threadId to
* zero.
*/
static StopReturnCode_t validateSprImageInputs( void* const i_pImage,
const CpuReg_t i_regId,
const uint32_t i_coreId,
uint32_t* i_pThreadId,
bool* i_pThreadLevelReg )
{
uint32_t index = 0;
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
bool sprSupported = false;
*i_pThreadLevelReg = false;
do
{
if( NULL == i_pImage )
{
// Error: HOMER image start location is not valid
// Cannot proceed further. So, let us exit.
l_rc = STOP_SAVE_ARG_INVALID_IMG;
MY_ERR( "invalid image location " );
break;
}
// STOP API manages STOP image based on physical core Id. PIR value
// is interpreted to calculate the physical core number and virtual
// thread number.
if( MAX_CORE_ID_SUPPORTED < i_coreId )
{
// Error: invalid core number. given core number exceeds maximum
// cores supported by chip.
// Physical core number is calculated based on following formula:
// core id = 4 * quad id (0..5) + core no within quad ( 0..3)
l_rc = STOP_SAVE_ARG_INVALID_CORE;
MY_ERR( "invalid core id " );
break;
}
if( MAX_THREAD_ID_SUPPORTED < *i_pThreadId )
{
//Error: invalid core thread. Given core thread exceeds maximum
//threads supported in a core.
// 64 bit PIR value is interpreted to calculate virtual thread
// Id. In fuse mode, b61 and b62 gives virtual thread id whereas in
// non fuse mode, b62 and b63 is read to determine the same.
l_rc = STOP_SAVE_ARG_INVALID_THREAD;
MY_ERR( "invalid thread " );
break;
}
for( index = 0; index < MAX_SPR_SUPPORTED; ++index )
{
if( i_regId == (CpuReg_t )g_sprRegister[index].sprId )
{
// given register is in the list of register supported
sprSupported = true;
*i_pThreadLevelReg = g_sprRegister[index].isThreadScope;
*i_pThreadId = *i_pThreadLevelReg ? *i_pThreadId : 0;
break;
}
}
if( !sprSupported )
{
// Following SPRs are supported
// trace out all registers supported
MY_ERR("Register not supported" );
// error code to caller.
l_rc = STOP_SAVE_ARG_INVALID_REG;
break;
}
}
while(0);
if( l_rc )
{
MY_ERR( "image 0x%08x, regId %08d, coreId %d, "
"threadId %d return code 0x%08x", i_pImage, i_regId,
i_coreId, *i_pThreadId, l_rc );
}
return l_rc;
}
//-----------------------------------------------------------------------------
/**
* @brief generates ori instruction code.
* @param[in] i_Rs Source register number
* @param[in] i_Ra destination register number
* @param[in] i_data 16 bit immediate data
* @return returns 32 bit number representing ori instruction.
*/
static uint32_t getOriInstruction( const uint16_t i_Rs, const uint16_t i_Ra,
const uint16_t i_data )
{
uint32_t oriInstOpcode = 0;
oriInstOpcode = 0;
oriInstOpcode = ORI_OPCODE << 26;
oriInstOpcode |= i_Rs << 21;
oriInstOpcode |= i_Ra << 16;
oriInstOpcode |= i_data;
return SWIZZLE_4_BYTE(oriInstOpcode);
}
//-----------------------------------------------------------------------------
/**
* @brief generates 32 bit key used for SPR lookup in core section.
*/
static uint32_t genKeyForSprLookup( const CpuReg_t i_regId )
{
return getOriInstruction( 0, 0, (uint16_t) i_regId );
}
//-----------------------------------------------------------------------------
/**
* @brief generates xor instruction code.
* @param[in] i_Rs source register number for xor operation
* @param[in] i_Ra destination register number for xor operation result
* @param[in] i_Rb source register number for xor operation
* @return returns 32 bit number representing xor immediate instruction.
*/
static uint32_t getXorInstruction( const uint16_t i_Ra, const uint16_t i_Rs,
const uint16_t i_Rb )
{
uint32_t xorRegInstOpcode;
xorRegInstOpcode = XOR_CONST << 1;
xorRegInstOpcode |= OPCODE_31 << 26;
xorRegInstOpcode |= i_Rs << 21;
xorRegInstOpcode |= i_Ra << 16;
xorRegInstOpcode |= i_Rb << 11;
return SWIZZLE_4_BYTE(xorRegInstOpcode);
}
//-----------------------------------------------------------------------------
/**
* @brief generates oris instruction code.
* @param[in] i_Rs source register number
* @param[in] i_Ra destination register number
* @param[in] i_data 16 bit immediate data
* @return returns 32 bit number representing oris immediate instruction.
*/
static uint32_t getOrisInstruction( const uint16_t i_Rs, const uint16_t i_Ra,
const uint16_t i_data )
{
uint32_t orisInstOpcode;
orisInstOpcode = 0;
orisInstOpcode = ORIS_OPCODE << 26;
orisInstOpcode |= ( i_Rs & 0x001F ) << 21 | ( i_Ra & 0x001F ) << 16;
orisInstOpcode |= i_data;
return SWIZZLE_4_BYTE(orisInstOpcode);
}
//-----------------------------------------------------------------------------
/**
* @brief generates instruction for mtspr
* @param[in] i_Rs source register number
* @param[in] i_Spr represents spr where data is to be moved.
* @return returns 32 bit number representing mtspr instruction.
*/
static uint32_t getMtsprInstruction( const uint16_t i_Rs, const uint16_t i_Spr )
{
uint32_t mtsprInstOpcode = 0;
uint32_t temp = (( i_Spr & 0x03FF ) << 11);
mtsprInstOpcode = (uint8_t)i_Rs << 21;
mtsprInstOpcode |= ( temp & 0x0000F800 ) << 5;
mtsprInstOpcode |= ( temp & 0x001F0000 ) >> 5;
mtsprInstOpcode |= MTSPR_BASE_OPCODE;
return SWIZZLE_4_BYTE(mtsprInstOpcode);
}
//-----------------------------------------------------------------------------
/**
* @brief generates rldicr instruction.
* @param[in] i_Rs source register number
* @param[in] i_Ra destination register number
* @param[in] i_sh bit position by which contents of i_Rs are to be shifted
* @param[in] i_me bit position up to which mask should be 1.
* @return returns 32 bit number representing rldicr instruction.
*/
static uint32_t getRldicrInstruction( const uint16_t i_Ra, const uint16_t i_Rs,
const uint16_t i_sh, uint16_t i_me )
{
uint32_t rldicrInstOpcode = 0;
rldicrInstOpcode = 0;
rldicrInstOpcode = ((RLDICR_OPCODE << 26 ) | ( i_Rs << 21 ) | ( i_Ra << 16 ));
rldicrInstOpcode |= ( ( i_sh & 0x001F ) << 11 ) | (RLDICR_CONST << 2 );
rldicrInstOpcode |= (( i_sh & 0x0020 ) >> 4);
rldicrInstOpcode |= (i_me & 0x001F ) << 6;
rldicrInstOpcode |= (i_me & 0x0020 );
return SWIZZLE_4_BYTE(rldicrInstOpcode);
}
//-----------------------------------------------------------------------------
/**
* @brief looks up entry for given SPR in given thread/core section.
* @param[in] i_pThreadSectLoc start of given thread section or core section.
* @param[in] i_lookUpKey search key for lookup of given SPR entry.
* @param[in] i_isCoreReg true if register is of scope core, false
* otherwise.
* @param[in|out] io_pSprEntryLoc Input: NULL
* Output: location of given entry or end of table.
* @return STOP_SAVE_SUCCESS if entry is found, STOP_SAVE_FAIL in case of
* an error.
*/
static StopReturnCode_t lookUpSprInImage( uint32_t* i_pThreadSectLoc,
const uint32_t i_lookUpKey,
const bool i_isCoreReg,
void** io_pSprEntryLoc )
{
StopReturnCode_t l_rc = STOP_SAVE_FAIL;
uint32_t temp = i_isCoreReg ? (uint32_t)(CORE_RESTORE_CORE_AREA_SIZE) :
(uint32_t)(CORE_RESTORE_THREAD_AREA_SIZE);
uint32_t* i_threadSectEnd = i_pThreadSectLoc + temp;
uint32_t bctr_inst = SWIZZLE_4_BYTE(BLR_INST);
*io_pSprEntryLoc = NULL;
do
{
if( !i_pThreadSectLoc )
{
break;
}
temp = 0;
while( ( i_pThreadSectLoc <= i_threadSectEnd ) &&
( temp != bctr_inst ) )
{
temp = *i_pThreadSectLoc;
if( ( temp == i_lookUpKey ) || ( temp == bctr_inst ) )
{
*io_pSprEntryLoc = i_pThreadSectLoc;
l_rc = STOP_SAVE_SUCCESS;
break;
}
i_pThreadSectLoc = i_pThreadSectLoc + SIZE_PER_SPR_RESTORE_INST;
}
}
while(0);
return l_rc;
}
//-----------------------------------------------------------------------------
/**
* @brief updates an SPR STOP image entry.
* @param[in] i_pSprEntryLocation location of entry.
* @param[in] i_regId register Id associated with SPR.
* @param[in] i_regData data needs to be written to SPR entry.
* @return STOP_SAVE_SUCCESS if update works, STOP_SAVE_FAIL otherwise.
*/
static StopReturnCode_t updateSprEntryInImage( uint32_t* i_pSprEntryLocation,
const CpuReg_t i_regId,
const uint64_t i_regData )
{
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
uint32_t tempInst = 0;
uint64_t tempRegData = 0;
bool newEntry = true;
uint16_t regRs = 0; //to use R0 for SPR restore insruction generation
uint16_t regRa = 0;
do
{
if( !i_pSprEntryLocation )
{
MY_ERR("invalid location of SPR image entry" );
l_rc = STOP_SAVE_FAIL;
break;
}
tempInst = genKeyForSprLookup( i_regId );
if( *i_pSprEntryLocation == tempInst )
{
newEntry = false;
}
//Add SPR search instruction i.e. "ori r0, r0, SPRID"
*i_pSprEntryLocation = tempInst;
i_pSprEntryLocation += SIZE_PER_SPR_RESTORE_INST;
//clear R0 i.e. "xor ra, rs, rb"
tempInst = getXorInstruction( regRs, regRs, regRs );
*i_pSprEntryLocation = tempInst;
i_pSprEntryLocation += SIZE_PER_SPR_RESTORE_INST;
tempRegData = i_regData >> 48;
//get lower order 16 bits of SPR restore value in R0
tempInst = getOrisInstruction( regRs, regRa, (uint16_t)tempRegData );
*i_pSprEntryLocation = tempInst;
i_pSprEntryLocation += SIZE_PER_SPR_RESTORE_INST;
tempRegData = ((i_regData >> 32) & 0x0000FFFF );
//get bit b16-b31 of SPR restore value in R0
tempInst = getOriInstruction( regRs, regRa, (uint16_t)tempRegData );
*i_pSprEntryLocation = tempInst;
i_pSprEntryLocation += SIZE_PER_SPR_RESTORE_INST;
//Rotate R0 to left by 32 bit position and zero lower order 32 bits.
//Place the result in R0
tempInst = getRldicrInstruction(regRa, regRs, 32, 31);
*i_pSprEntryLocation = tempInst;
i_pSprEntryLocation += SIZE_PER_SPR_RESTORE_INST;
tempRegData = ((i_regData >> 16) & 0x000000FFFF );
//get bit b32-b47 of SPR restore value to R0
tempInst = getOrisInstruction( regRs, regRa, (uint16_t)tempRegData );
*i_pSprEntryLocation = tempInst;
i_pSprEntryLocation += SIZE_PER_SPR_RESTORE_INST;
tempRegData = (uint16_t)i_regData;
//get bit b48-b63 of SPR restore value to R0
tempInst = getOriInstruction( regRs, regRa, (uint16_t)i_regData );
*i_pSprEntryLocation = tempInst;
i_pSprEntryLocation += SIZE_PER_SPR_RESTORE_INST;
if( P9_STOP_SPR_MSR == i_regId )
{
//MSR cannot be restored completely with mtmsrd instruction.
//as it does not update ME, LE and HV bits. In self restore code
//inorder to restore MSR, contents of R21 is moved to SRR1. It also
//executes an RFID which causes contents of SRR1 to be copied to
//MSR. This allows copy of LE bit which are specifically interested
//in. Instruction below moves contents of MSR Value (in R0 ) to R21.
tempInst = SWIZZLE_4_BYTE( MR_R0_TO_R21 );
}
else if (P9_STOP_SPR_HRMOR == i_regId )
{
//Case HRMOR, move contents of R0 to a placeholder GPR (R10)
//Thread Launcher expects HRMOR value in R10
tempInst = SWIZZLE_4_BYTE( MR_R0_TO_R10 );
}
else
{
// Case other SPRs, move contents of R0 to SPR
tempInst =
getMtsprInstruction( 0, (uint16_t)i_regId );
}
*i_pSprEntryLocation = tempInst;
if( newEntry )
{
i_pSprEntryLocation += SIZE_PER_SPR_RESTORE_INST;
//at the end of SPR restore, add instruction BLR to go back to thread
//launcher.
tempInst = SWIZZLE_4_BYTE(BLR_INST);
*i_pSprEntryLocation = tempInst;
}
}
while(0);
return l_rc;
}
//-----------------------------------------------------------------------------
StopReturnCode_t p9_stop_save_cpureg( void* const i_pImage,
const CpuReg_t i_regId,
const uint64_t i_regData,
const uint64_t i_pir )
{
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS; // procedure return code
HomerSection_t* chipHomer = NULL;
do
{
uint32_t threadId = 0;
uint32_t coreId = 0;
uint32_t lookUpKey = 0;
void* pSprEntryLocation = NULL; // an offset w.r.t. to start of image
void* pThreadLocation = NULL;
bool threadScopeReg = false;
l_rc = getCoreAndThread( i_pImage, i_pir, &coreId, &threadId );
if( l_rc )
{
MY_ERR("Failed to determine Core Id and Thread Id from PIR 0x%016llx",
i_pir);
break;
}
MY_INF( " PIR 0x%016llx coreId %d threadid %d "
" registerId %d", i_pir, coreId,
threadId, i_regId );
// First of all let us validate all input arguments.
l_rc = validateSprImageInputs( i_pImage,
i_regId,
coreId,
&threadId,
&threadScopeReg );
if( l_rc )
{
// Error: bad argument traces out error code
MY_ERR("Bad input argument rc %d", l_rc );
break;
}
chipHomer = ( HomerSection_t*)i_pImage;
if( threadScopeReg )
{
pThreadLocation =
&(chipHomer->coreThreadRestore[coreId][threadId].threadArea[0]);
}
else
{
pThreadLocation =
&(chipHomer->coreThreadRestore[coreId][threadId].coreArea[0]);
}
if( ( SWIZZLE_4_BYTE(BLR_INST) == *(uint32_t*)pThreadLocation ) ||
( SWIZZLE_4_BYTE(ATTN_OPCODE) == *(uint32_t*) pThreadLocation ) )
{
// table for given core id doesn't exit. It needs to be
// defined.
pSprEntryLocation = pThreadLocation;
}
else
{
// an SPR restore section for given core already exists
lookUpKey = genKeyForSprLookup( i_regId );
l_rc = lookUpSprInImage( (uint32_t*)pThreadLocation,
lookUpKey,
threadScopeReg,
&pSprEntryLocation );
}
if( l_rc )
{
MY_ERR("Invalid or corrupt SPR entry. CoreId 0x%08x threadId ",
"0x%08x regId 0x%08x lookUpKey 0x%08x pThreadLocation 0x%08x"
, coreId, threadId, i_regId, lookUpKey, pThreadLocation );
break;
}
l_rc = updateSprEntryInImage( (uint32_t*) pSprEntryLocation,
i_regId,
i_regData );
if( l_rc )
{
MY_ERR( " Failed to update the SPR entry of PIR 0x%08x reg"
"0x%08x", i_pir, i_regId );
break;
}
}
while(0);
return l_rc;
}
//-----------------------------------------------------------------------------
/**
* @brief validates all the input arguments.
* @param[in] i_pImage pointer to start of HOMER of image for proc chip.
* @param[in] i_scomAddress SCOM address of register.
* @param[in] i_chipletId core or cache chiplet id
* @param[in] i_operation operation requested for SCOM entry.
* @param[in] i_section image section on which operation is to be performed
* @return STOP_SAVE_SUCCESS if arguments found valid, error code otherwise.
* @note Function does not validate that the given SCOM address really
* belongs to the given section.
*/
static StopReturnCode_t validateScomImageInputs( void* const i_pImage,
const uint32_t i_scomAddress,
const uint8_t i_chipletId,
const ScomOperation_t i_operation,
const ScomSection_t i_section )
{
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
do
{
if( !i_pImage )
{
//Error Invalid image pointer
l_rc = STOP_SAVE_ARG_INVALID_IMG;
MY_ERR("invalid image location ");
break;
}
if( 0 == i_scomAddress )
{
l_rc = STOP_SAVE_SCOM_INVALID_ADDRESS;
MY_ERR("invalid SCOM address");
break;
}
if(( CACHE_CHIPLET_ID_MIN > i_chipletId ) ||
( CORE_CHIPLET_ID_MAX < i_chipletId ))
{
l_rc = STOP_SAVE_SCOM_INVALID_CHIPLET;
MY_ERR("chiplet id not in range");
break;
}
if(( CORE_CHIPLET_ID_MIN > i_chipletId ) &&
( CACHE_CHIPLET_ID_MAX < i_chipletId ))
{
l_rc = STOP_SAVE_SCOM_INVALID_CHIPLET;
MY_ERR("chiplet id not valid");
break;
}
if(( P9_STOP_SCOM_OP_MIN >= i_operation ) ||
( P9_STOP_SCOM_OP_MAX <= i_operation ))
{
//invalid SCOM image operation requested
l_rc = STOP_SAVE_SCOM_INVALID_OPERATION;
MY_ERR("invalid SCOM image operation");
break;
}
if(( P9_STOP_SECTION_MIN >= i_section ) ||
( P9_STOP_SECTION_MAX <= i_section ))
{
// invalid cache sub section specified
l_rc = STOP_SAVE_SCOM_INVALID_SECTION;
MY_ERR("invalid section");
break;
}
if(( i_operation == P9_STOP_SCOM_RESET ) &&
( i_chipletId < CORE_CHIPLET_ID_MIN ))
{
// replace requested with a cache chiplet Id
l_rc = STOP_SAVE_SCOM_INVALID_OPERATION;
MY_ERR( "reset not supported for cache. chiplet Id 0x%08x",
i_chipletId );
break;
}
}
while(0);
if( l_rc )
{
MY_ERR("image 0x%08x SCOMAddress 0x%08x chipletId 0x%08x operation"
"0x%08x section 0x%08x", i_pImage, i_scomAddress, i_chipletId,
i_operation, i_section );
}
return l_rc;
}
//-----------------------------------------------------------------------------
/**
* @brief edit SCOM entry associated with the given core.
* @param[in] i_scomAddr SCOM address of register.
* @param[in] i_scomData data associated with SCOM register.
* @param[in] i_pEntryLocation points to a SCOM entry in HOMER image.
* @param[in] i_operation operation to be performed on SCOM entry.
* @return STOP_SAVE_SUCCESS if existing entry is updated, STOP_SAVE_FAIL
* otherwise.
*/
static StopReturnCode_t editScomEntry( uint32_t i_scomAddr, uint64_t i_scomData,
ScomEntry_t* i_pEntryLocation,
uint32_t i_operation )
{
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
do
{
if( !i_pEntryLocation )
{
//Error: location of SCOM entry is not known
//therefore no point moving forward
MY_ERR("SCOM entry location not valid");
l_rc = STOP_SAVE_FAIL;
break;
}
switch( i_operation )
{
case P9_STOP_SCOM_OR:
i_pEntryLocation->scomEntryData |= i_scomData;
break;
case P9_STOP_SCOM_AND:
i_pEntryLocation->scomEntryData &= i_scomData;
break;
case P9_STOP_SCOM_NOOP:
{
uint32_t nopInst = getOriInstruction( 0, 0, 0 );
i_pEntryLocation->scomEntryHeader = SWIZZLE_4_BYTE(SCOM_ENTRY_START);
i_pEntryLocation->scomEntryData = nopInst;
i_pEntryLocation->scomEntryAddress = nopInst;
}
break;
case P9_STOP_SCOM_APPEND:
i_pEntryLocation->scomEntryHeader = SWIZZLE_4_BYTE(SCOM_ENTRY_START);
i_pEntryLocation->scomEntryData = i_scomData;
i_pEntryLocation->scomEntryAddress = i_scomAddr;
break;
}
}
while(0);
return l_rc;
}
//-----------------------------------------------------------------------------
/**
* @brief update SCOM entry associated with the given core.
* @param[in] i_scomAddr SCOM address of register.
* @param[in] i_scomData data associated with SCOM register.
* @param[in] i_scomEntry points to a SCOM entry in cache section of HOMER image.
* @return STOP_SAVE_SUCCESS if new entry is added, STOP_SAVE_FAIL otherwise.
* @note adds an entry at a given location. It can be used to add entry in
* place of NOP, at the end of table or as first entry of the cache
* sub-section(L2, L3 or EQ ).
*/
static StopReturnCode_t updateScomEntry( uint32_t i_scomAddr, uint64_t i_scomData,
ScomEntry_t* i_scomEntry )
{
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
do
{
if( !i_scomEntry )
{
MY_ERR( "cache entry cannot be located");
l_rc = STOP_SAVE_SCOM_ENTRY_UPDATE_FAILED;
break;
}
i_scomEntry->scomEntryHeader = SWIZZLE_4_BYTE(SCOM_ENTRY_START); // done for now
i_scomEntry->scomEntryAddress = i_scomAddr;
i_scomEntry->scomEntryData = i_scomData;
}
while(0);
return l_rc;
}
//-----------------------------------------------------------------------------
/**
* @brief populates SCOM restore entry header with version and layout info.
* @param[in] i_scomEntry points to SCOM restore entry
* @param[in] i_section section of SCOM restore area
* @param[in] i_imageVer SGPE image version
*/
static void updateEntryHeader( ScomEntry_t* i_scomEntry ,
const ScomSection_t i_section,
uint32_t i_imageVer )
{
uint32_t l_temp = 0;
uint32_t l_entryLimit = 0;
if( i_imageVer >= STOP_API_VER_CONTROL )
{
if( P9_STOP_SECTION_CORE_SCOM == i_section )
{
l_entryLimit = MAX_CORE_SCOM_ENTRIES;
}
else
{
l_entryLimit = MAX_EQ_SCOM_ENTRIES + MAX_L2_SCOM_ENTRIES +
MAX_L3_SCOM_ENTRIES;
}
l_temp |= ( STOP_API_VER & 0x7 ) << 28;
l_temp |= ( 0x000000ff & l_entryLimit );
i_scomEntry->scomEntryHeader = SWIZZLE_4_BYTE(l_temp);
MY_INF("SCOM Restore Header 0x%08x", l_temp );
}
}
//-----------------------------------------------------------------------------
StopReturnCode_t p9_stop_save_scom( void* const i_pImage,
const uint32_t i_scomAddress,
const uint64_t i_scomData,
const ScomOperation_t i_operation,
const ScomSection_t i_section )
{
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
StopCacheSection_t* pStopCacheScomStart = NULL;
ScomEntry_t* pScomEntry = NULL;
uint32_t entryLimit = 0;
uint8_t chipletId = 0;
uint32_t nopInst;
ScomEntry_t* pEntryLocation = NULL;
ScomEntry_t* pNopLocation = NULL;
ScomEntry_t* pEditScomHeader = NULL;
ScomEntry_t* pTableEndLocationtable = NULL;
uint32_t swizzleAddr;
uint64_t swizzleData;
uint32_t swizzleAttn;
uint32_t entrySwzHeader = 0;
uint32_t index = 0;
uint32_t swizzleBlr = SWIZZLE_4_BYTE(BLR_INST);
//Reads SGPE image version info from QPMR Header in HOMER
//For backward compatibility, for base version of SGPE Hcode,
//STOP API retains default behavior but adds version specific
//details in each entry in later versions.
uint32_t imageVer = *(uint32_t*)((uint8_t*)i_pImage + QPMR_HOMER_OFFSET + QPMR_BUILD_VER_BYTE);
imageVer = SWIZZLE_4_BYTE(imageVer);
do
{
chipletId = i_scomAddress >> 24;
chipletId = chipletId & 0x3F;
l_rc = validateScomImageInputs( i_pImage,
i_scomAddress,
chipletId,
i_operation,
i_section );
if( l_rc )
{
MY_ERR( "invalid argument: aborting");
break;
}
if( chipletId >= CORE_CHIPLET_ID_MIN )
{
// chiplet is core. So, let us find the start address of SCOM area
// pertaining to a core in STOP image.
pScomEntry = CORE_ID_SCOM_START(i_pImage,
chipletId )
entryLimit = MAX_CORE_SCOM_ENTRIES;
}
else
{
// chiplet is a cache. let us find start address of cache section
// associated with given chiplet. A cache section associated with
// given chiplet is split in to L2, L3 and EQ area.
pStopCacheScomStart = CACHE_SECTN_START(i_pImage,
chipletId);
}
if(( !pStopCacheScomStart ) && ( !pScomEntry) )
{
//Error invalid pointer to SCOM entry in cache or core section
//of STOP image.
MY_ERR("invalid start location for chiplet %d",
chipletId );
break;
}
switch( i_section )
{
case P9_STOP_SECTION_EQ_SCOM:
pScomEntry = pStopCacheScomStart->nonCacheArea;
entryLimit = MAX_EQ_SCOM_ENTRIES;
break;
case P9_STOP_SECTION_L2:
pScomEntry = pStopCacheScomStart->l2CacheArea;
entryLimit = MAX_L2_SCOM_ENTRIES;
break;
case P9_STOP_SECTION_L3:
pScomEntry = pStopCacheScomStart->l3CacheArea;
entryLimit = MAX_L3_SCOM_ENTRIES;
break;
case P9_STOP_SECTION_CORE_SCOM:
//macro CORE_ID_SCOM_START already gives start of scom
//entry for given core. entry limit too is assigned thereafter.
//Handling for core and cache segment is different for scom
//entries. It is because scom entries are organized differently
//in core and cache segment.
break;
default:
l_rc = STOP_SAVE_SCOM_INVALID_SECTION;
break;
}
if(( !pScomEntry ) || ( l_rc ) )
{
// Error Invalid pointer to cache entry
MY_ERR("invalid subsection %d or internal firmware failure",
i_section );
l_rc = STOP_SAVE_FAIL;
break;
}
nopInst = getOriInstruction( 0, 0, 0 );
pEntryLocation = NULL;
pNopLocation = NULL;
pTableEndLocationtable = NULL;
swizzleAddr = SWIZZLE_4_BYTE(i_scomAddress);
swizzleData = SWIZZLE_8_BYTE(i_scomData);
swizzleAttn = SWIZZLE_4_BYTE(ATTN_OPCODE);
for( index = 0; index < entryLimit; ++index )
{
uint32_t entrySwzAddress = pScomEntry[index].scomEntryAddress;
entrySwzHeader = SWIZZLE_4_BYTE(pScomEntry[index].scomEntryHeader);
if( ( swizzleAddr == entrySwzAddress ) && ( !pEntryLocation ) )
{
pEntryLocation = &pScomEntry[index];
}
if( (( nopInst == entrySwzAddress ) ||
( swizzleAttn == entrySwzAddress ) ||
( swizzleBlr == entrySwzAddress )) && ( !pNopLocation ) )
{
pNopLocation = &pScomEntry[index];
}
// if entry is either 0xDEADDEAD or has SCOM entry limit in LSB of header
// place is already occupied
if( ( SCOM_ENTRY_START == entrySwzHeader ) ||
( entrySwzHeader & 0x000000FF ) )
{
continue;
}
pTableEndLocationtable = &pScomEntry[index];
break;
}
if( ( !pEntryLocation ) && ( !pTableEndLocationtable ) )
{
MY_ERR(" exhausted all location available for section"
"0x%08x scom address 0x%08x",
i_section, i_scomAddress );
l_rc = STOP_SAVE_SCOM_ENTRY_UPDATE_FAILED;
break;
}
switch( i_operation )
{
case P9_STOP_SCOM_APPEND:
{
ScomEntry_t* pScomAppend = NULL;
if( pNopLocation )
{
pScomAppend = pNopLocation;
}
else
{
pScomAppend = pTableEndLocationtable;
}
l_rc = updateScomEntry ( swizzleAddr,
swizzleData, pScomAppend );
pEditScomHeader = pScomAppend;
}
break;
case P9_STOP_SCOM_REPLACE:
{
ScomEntry_t* scomReplace = NULL;
if( pEntryLocation )
{
scomReplace = pEntryLocation;
}
else
{
scomReplace = pTableEndLocationtable;
}
l_rc = updateScomEntry( swizzleAddr,
swizzleData, scomReplace );
pEditScomHeader = scomReplace;
}
break;
case P9_STOP_SCOM_OR:
case P9_STOP_SCOM_AND:
case P9_STOP_SCOM_NOOP:
if( pEntryLocation )
{
l_rc = editScomEntry( swizzleAddr,
swizzleData,
pEntryLocation,
i_operation );
pEditScomHeader = pEntryLocation;
}
else
{
//Invalid operation requested.
MY_ERR( "entry not found edit chiplet Id 0x%08x "
"swizzle addr 0x%08x ",
chipletId, swizzleAddr );
l_rc = STOP_SAVE_SCOM_INVALID_OPERATION;
}
break;
case P9_STOP_SCOM_RESET:
if( P9_STOP_SECTION_CORE_SCOM == i_section )
{
memset( pScomEntry, 0x00, CORE_SCOM_RESTORE_SIZE_PER_CORE );
}
break;
case P9_STOP_SCOM_OR_APPEND:
case P9_STOP_SCOM_AND_APPEND:
{
uint32_t tempOperation = P9_STOP_SCOM_APPEND;
ScomEntry_t* editAppend = NULL;
if( NULL == pEntryLocation )
{
editAppend = pTableEndLocationtable;
}
else
{
editAppend = pEntryLocation;
if( P9_STOP_SCOM_OR_APPEND == i_operation )
{
tempOperation = P9_STOP_SCOM_OR;
}
else
{
tempOperation = P9_STOP_SCOM_AND;
}
}
l_rc = editScomEntry( swizzleAddr,
swizzleData,
editAppend,
tempOperation );
pEditScomHeader = editAppend;
}
break;
default:
l_rc = STOP_SAVE_SCOM_INVALID_OPERATION;
break;
}
}
while(0);
if( l_rc )
{
MY_ERR("SCOM image operation 0x%08x failed for chiplet 0x%08x addr"
"0x%08x", i_operation, chipletId ,
i_scomAddress );
}
else
{
//Update SCOM Restore entry with version and memory layout
//info
updateEntryHeader( pEditScomHeader, i_section, imageVer );
}
return l_rc;
}
#ifdef __cplusplus
} //namespace stopImageSection ends
} //extern "C"
#endif