1743 lines
59 KiB
C
1743 lines
59 KiB
C
/* 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,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 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
|
|
|
|
// *INDENT-OFF*
|
|
#ifdef PPC_HYP
|
|
#include <HvPlicModule.H>
|
|
#endif
|
|
|
|
#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"
|
|
#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_CIABR, true, 0 },
|
|
{ P9_STOP_SPR_DAWR, true, 1 },
|
|
{ P9_STOP_SPR_DAWRX, true, 2 },
|
|
{ P9_STOP_SPR_HSPRG0, true, 3 },
|
|
{ P9_STOP_SPR_LDBAR, true, 4, },
|
|
{ P9_STOP_SPR_LPCR, true, 5 },
|
|
{ P9_STOP_SPR_PSSCR, true, 6 },
|
|
{ P9_STOP_SPR_MSR, true, 7 },
|
|
{ P9_STOP_SPR_HRMOR, false, 20 },
|
|
{ P9_STOP_SPR_HID, false, 21 },
|
|
{ P9_STOP_SPR_HMEER, false, 22 },
|
|
{ P9_STOP_SPR_PMCR, false, 23 },
|
|
{ P9_STOP_SPR_PTCR, false, 24 },
|
|
{ P9_STOP_SPR_SMFCTRL, true, 28 },
|
|
{ P9_STOP_SPR_USPRG0, true, 29 },
|
|
{ P9_STOP_SPR_USPRG1, true, 30 },
|
|
{ P9_STOP_SPR_URMOR, false, 31 },
|
|
};
|
|
|
|
const uint32_t MAX_SPR_SUPPORTED = 17;
|
|
const uint32_t LEGACY_CORE_SCOM_SUPPORTED = 15;
|
|
const uint32_t LEGACY_QUAD_SCOM_SUPPORTED = 63;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @brief vaildated input arguments passed to p9_stop_save_cpureg_control.
|
|
* @param[in] i_pImage point to start of HOMER
|
|
* @param[in] i_coreId id of the core
|
|
* @param[in] i_threadId id of the thread
|
|
* @param[in] i_saveMaskVector SPR save bit mask vector
|
|
* @return STOP_SAVE_SUCCESS if function succeeds, error code otherwise.
|
|
*/
|
|
STATIC StopReturnCode_t validateArgumentSaveRegMask( void* const i_pImage,
|
|
uint32_t const i_coreId,
|
|
uint32_t const i_threadId,
|
|
uint64_t i_saveMaskVector )
|
|
{
|
|
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
|
|
|
|
do
|
|
{
|
|
if( !i_pImage )
|
|
{
|
|
l_rc = STOP_SAVE_ARG_INVALID_IMG;
|
|
break;
|
|
}
|
|
|
|
if( i_coreId > MAX_CORE_ID_SUPPORTED )
|
|
{
|
|
l_rc = STOP_SAVE_ARG_INVALID_CORE;
|
|
break;
|
|
}
|
|
|
|
if( i_threadId > MAX_THREAD_ID_SUPPORTED )
|
|
{
|
|
l_rc = STOP_SAVE_ARG_INVALID_THREAD;
|
|
break;
|
|
}
|
|
|
|
if( ( 0 == i_saveMaskVector ) || ( BAD_SAVE_MASK & i_saveMaskVector ) )
|
|
{
|
|
l_rc = STOP_SAVE_ARG_INVALID_REG;
|
|
break;
|
|
}
|
|
|
|
}
|
|
while(0);
|
|
|
|
return l_rc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @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].iv_sprId )
|
|
{
|
|
// given register is in the list of register supported
|
|
sprSupported = true;
|
|
*i_pThreadLevelReg = g_sprRegister[index].iv_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 instruction for mfmsr
|
|
* @param[in] i_Rt target register for SPR content.
|
|
* @return returns 32 bit number representing mfmsr instruction.
|
|
*/
|
|
STATIC uint32_t getMfmsrInstruction( const uint16_t i_Rt )
|
|
{
|
|
uint32_t mfmsrInstOpcode = ((OPCODE_31 << 26) | (i_Rt << 21) | (MFMSR_CONST));
|
|
|
|
return SWIZZLE_4_BYTE(mfmsrInstOpcode);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @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 = ((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);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
STATIC uint32_t getMfsprInstruction( const uint16_t i_Rt, const uint16_t i_sprNum )
|
|
{
|
|
uint32_t mfsprInstOpcode = 0;
|
|
mfsprInstOpcode = (( OPCODE_31 << 26 ) | ( i_Rt << 21 ) | ( i_sprNum << 11 ) | ( MFSPR_CONST << 1 ));
|
|
return SWIZZLE_4_BYTE(mfsprInstOpcode);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
STATIC uint32_t getBranchLinkRegInstruction(void)
|
|
{
|
|
uint32_t branchConstInstOpcode = 0;
|
|
branchConstInstOpcode = (( OPCODE_18 << 26 ) | ( SELF_SAVE_FUNC_ADD ) | 0x03 );
|
|
|
|
return SWIZZLE_4_BYTE(branchConstInstOpcode);
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @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_isThreadReg true if register is of scope thread, 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_isThreadReg, void** io_pSprEntryLoc,
|
|
uint8_t i_selfRestVer )
|
|
{
|
|
StopReturnCode_t l_rc = STOP_SAVE_FAIL;
|
|
uint32_t temp = 0;
|
|
uint32_t* i_threadSectEnd = NULL;
|
|
uint32_t bctr_inst = SWIZZLE_4_BYTE(BLR_INST);
|
|
*io_pSprEntryLoc = NULL;
|
|
|
|
do
|
|
{
|
|
if( !i_pThreadSectLoc )
|
|
{
|
|
MY_ERR( "Bad SPR Start Location" );
|
|
break;
|
|
}
|
|
|
|
if( i_selfRestVer )
|
|
{
|
|
temp = i_isThreadReg ? (uint32_t)(SMF_CORE_RESTORE_THREAD_AREA_SIZE) :
|
|
(uint32_t)(SMF_CORE_RESTORE_CORE_AREA_SIZE);
|
|
|
|
}
|
|
else
|
|
{
|
|
temp = i_isThreadReg ? (uint32_t)(CORE_RESTORE_THREAD_AREA_SIZE) :
|
|
(uint32_t)(CORE_RESTORE_CORE_AREA_SIZE);
|
|
}
|
|
|
|
|
|
i_threadSectEnd = i_pThreadSectLoc + ( temp >> 2 );
|
|
|
|
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,
|
|
const enum SprEntryUpdateMode i_mode
|
|
)
|
|
{
|
|
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;
|
|
|
|
if( INIT_SPR_REGION == i_mode )
|
|
{
|
|
//adding inst 'b . + 0x1C'
|
|
*i_pSprEntryLocation = SWIZZLE_4_BYTE(SKIP_SPR_REST_INST);
|
|
}
|
|
else
|
|
{
|
|
//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 if( P9_STOP_SPR_URMOR == i_regId )
|
|
{
|
|
//Case URMOR, move contents of R0 to a placeholder GPR (R9)
|
|
//Thread Launcher expects URMOR value in R9
|
|
tempInst = SWIZZLE_4_BYTE( MR_R0_TO_R9 );
|
|
}
|
|
else
|
|
{
|
|
// Case other SPRs, move contents of R0 to SPR
|
|
// For a UV system, even HRMOR is treated like any other 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;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
STATIC StopReturnCode_t initSelfSaveEntry( void* const i_pImage, uint16_t i_sprNum )
|
|
{
|
|
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
|
|
uint32_t* i_pSprSave = (uint32_t*)i_pImage;
|
|
|
|
//ori r0, r0, 0x00nn
|
|
*i_pSprSave = getOriInstruction( 0, 0, i_sprNum );
|
|
|
|
i_pSprSave++;
|
|
|
|
//addi r31, r31, 0x20
|
|
*i_pSprSave = SWIZZLE_4_BYTE(SKIP_SPR_SELF_SAVE);
|
|
i_pSprSave++;
|
|
|
|
//nop
|
|
*i_pSprSave = getOriInstruction( 0, 0, 0 );;
|
|
i_pSprSave++;
|
|
|
|
//mtlr, r30
|
|
*i_pSprSave = SWIZZLE_4_BYTE( MTLR_INST );
|
|
i_pSprSave++;
|
|
|
|
//blr
|
|
*i_pSprSave = SWIZZLE_4_BYTE(BLR_INST);
|
|
i_pSprSave++;
|
|
|
|
return l_rc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
STATIC StopReturnCode_t getSprRegIndexAdjustment( const uint32_t i_saveMaskPos, uint32_t* i_sprAdjIndex )
|
|
{
|
|
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
|
|
|
|
do
|
|
{
|
|
if( (( i_saveMaskPos >= SPR_BIT_POS_8 ) && ( i_saveMaskPos <= SPR_BIT_POS_19 )) ||
|
|
(( i_saveMaskPos >= SPR_BIT_POS_25 ) && ( i_saveMaskPos <= SPR_BIT_POS_27 )) )
|
|
{
|
|
l_rc = STOP_SAVE_SPR_BIT_POS_RESERVE;
|
|
break;
|
|
}
|
|
|
|
if( (i_saveMaskPos > SPR_BIT_POS_19) && (i_saveMaskPos < SPR_BIT_POS_25 ) )
|
|
{
|
|
*i_sprAdjIndex = 12;
|
|
}
|
|
else if( i_saveMaskPos > SPR_BIT_POS_27 )
|
|
{
|
|
*i_sprAdjIndex = 15;
|
|
}
|
|
else
|
|
{
|
|
*i_sprAdjIndex = 0;
|
|
}
|
|
|
|
}
|
|
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;
|
|
SmfHomerSection_t* smfChipHomer = 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;
|
|
uint8_t l_urmorFix = false;
|
|
uint64_t l_sprValue = 0;
|
|
uint8_t l_selfRestVer = 0;
|
|
|
|
MY_INF(">> p9_stop_save_cpureg" );
|
|
|
|
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;
|
|
}
|
|
|
|
l_urmorFix = *(uint8_t*)((uint8_t*)i_pImage + CPMR_HOMER_OFFSET + CPMR_URMOR_FIX_BYTE);
|
|
l_selfRestVer = *(uint8_t *)((uint8_t *)i_pImage + CPMR_HOMER_OFFSET + CPMR_SELF_RESTORE_VER_BYTE );
|
|
|
|
if( l_selfRestVer )
|
|
{
|
|
smfChipHomer = ( SmfHomerSection_t*)i_pImage;
|
|
|
|
if( threadScopeReg )
|
|
{
|
|
pThreadLocation =
|
|
&(smfChipHomer->iv_coreThreadRestore[coreId].iv_threadRestoreArea[threadId][0]);
|
|
}
|
|
else
|
|
{
|
|
pThreadLocation =
|
|
&(smfChipHomer->iv_coreThreadRestore[coreId].iv_coreRestoreArea[0]);
|
|
}
|
|
}
|
|
else //Old fips or OPAL release that doesn't support SMF
|
|
{
|
|
chipHomer = (HomerSection_t*)i_pImage;
|
|
|
|
if( threadScopeReg )
|
|
{
|
|
pThreadLocation =
|
|
&(chipHomer->iv_coreThreadRestore[coreId][threadId].iv_threadArea[0]);
|
|
}
|
|
else
|
|
{
|
|
pThreadLocation =
|
|
&(chipHomer->iv_coreThreadRestore[coreId][threadId].iv_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,
|
|
l_selfRestVer );
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if( ( P9_STOP_SPR_URMOR == i_regId ) && ( l_urmorFix ) )
|
|
{
|
|
l_sprValue = i_regData - URMOR_CORRECTION;
|
|
}
|
|
else
|
|
{
|
|
l_sprValue = i_regData;
|
|
}
|
|
|
|
l_rc = updateSprEntryInImage( (uint32_t*) pSprEntryLocation,
|
|
i_regId,
|
|
l_sprValue,
|
|
UPDATE_SPR_ENTRY );
|
|
|
|
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);
|
|
|
|
MY_INF("<< p9_stop_save_cpureg" );
|
|
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_imageVer SGPE image version
|
|
* @param[in] i_maxScomEntry max SCOM entries supported
|
|
*/
|
|
|
|
STATIC void updateEntryHeader( ScomEntry_t* i_scomEntry ,
|
|
uint32_t i_imageVer,
|
|
uint32_t i_maxScomEntry )
|
|
{
|
|
uint32_t l_temp = 0;
|
|
|
|
if( i_imageVer >= STOP_API_VER_CONTROL )
|
|
{
|
|
l_temp = ( 0x000000ff & i_maxScomEntry );
|
|
l_temp |= ( STOP_API_VER & 0x7 ) << 28;
|
|
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;
|
|
uint32_t entryLimit = 0;
|
|
uint8_t chipletId = 0;
|
|
uint32_t nopInst = 0;
|
|
uint32_t index = 0;
|
|
uint32_t imageVer = 0;
|
|
uint32_t entrySwzHeader = 0;
|
|
uint32_t l_maxScomRestoreEntry = 0;
|
|
ScomEntry_t* pScomEntry = NULL;
|
|
ScomEntry_t* pEntryLocation = NULL;
|
|
ScomEntry_t* pNopLocation = NULL;
|
|
ScomEntry_t* pEditScomHeader = NULL;
|
|
StopCacheSection_t* pStopCacheScomStart = NULL;
|
|
ScomEntry_t* pTableEndLocationtable = NULL;
|
|
uint32_t swizzleAddr;
|
|
uint64_t swizzleData;
|
|
uint32_t swizzleAttn;
|
|
uint32_t swizzleBlr = SWIZZLE_4_BYTE(BLR_INST);
|
|
bool cacheEntry = true;
|
|
|
|
MY_INF(">> p9_stop_save_scom");
|
|
|
|
//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.
|
|
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.
|
|
l_maxScomRestoreEntry =
|
|
*(uint32_t*)((uint8_t*)i_pImage + CPMR_HOMER_OFFSET + CPMR_MAX_SCOM_REST_PER_CORE_BYTE);
|
|
pScomEntry = CORE_ID_SCOM_START(i_pImage, chipletId )
|
|
cacheEntry = false;
|
|
|
|
if( !l_maxScomRestoreEntry )
|
|
{
|
|
//Old HB and new STOP API case. Retain legacy Number
|
|
l_maxScomRestoreEntry = SWIZZLE_4_BYTE(LEGACY_CORE_SCOM_SUPPORTED);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
l_maxScomRestoreEntry =
|
|
*(uint32_t*)((uint8_t*)i_pImage + QPMR_HOMER_OFFSET + QPMR_QUAD_MAX_SCOM_ENTRY_BYTE);
|
|
|
|
if( !l_maxScomRestoreEntry )
|
|
{
|
|
// Incase of a bad HOMER header initialization, fall back on legacy number.
|
|
l_maxScomRestoreEntry = SWIZZLE_4_BYTE(LEGACY_QUAD_SCOM_SUPPORTED);
|
|
}
|
|
// 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);
|
|
}
|
|
|
|
l_maxScomRestoreEntry = SWIZZLE_4_BYTE(l_maxScomRestoreEntry);
|
|
|
|
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.
|
|
|
|
entryLimit = l_maxScomRestoreEntry;
|
|
break;
|
|
|
|
default:
|
|
l_rc = STOP_SAVE_SCOM_INVALID_SECTION;
|
|
break;
|
|
}
|
|
|
|
|
|
if(( imageVer > LEGACY_SCOM_RESTORE_VER ) && ( cacheEntry ) )
|
|
{
|
|
//STOP API migrated to newer algorithm for creation of entries
|
|
|
|
pScomEntry = CACHE_SCOM_ADDR(i_pImage,
|
|
chipletId,
|
|
l_maxScomRestoreEntry )
|
|
|
|
entryLimit = l_maxScomRestoreEntry;
|
|
}
|
|
|
|
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, imageVer, l_maxScomRestoreEntry );
|
|
}
|
|
|
|
MY_INF("<< p9_stop_save_scom");
|
|
return l_rc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @brief searches a self save entry of an SPR in self-save segment.
|
|
* @param[in] i_sprBitPos bit position associated with SPR in save mask vector.
|
|
* @param[in] l_pSprSaveStart start location of SPR save segment
|
|
* @param[in] i_searchLength length of SPR save segment
|
|
* @param[in] i_pSaveSprLoc start location of save entry for a given SPR.
|
|
* @return STOP_SAVE_SUCCESS if look up succeeds, error code otherwise.
|
|
*/
|
|
STATIC StopReturnCode_t lookUpSelfSaveSpr( uint32_t i_sprBitPos, uint32_t* l_pSprSaveStart,
|
|
uint32_t i_searchLength, uint32_t** i_pSaveSprLoc )
|
|
{
|
|
int32_t l_saveWordLength = (int32_t)(i_searchLength >> 2);
|
|
uint32_t l_oriInst = getOriInstruction( 0, 0, i_sprBitPos );
|
|
StopReturnCode_t l_rc = STOP_SAVE_FAIL;
|
|
|
|
while( l_saveWordLength > 0 )
|
|
{
|
|
if( l_oriInst == *l_pSprSaveStart )
|
|
{
|
|
*i_pSaveSprLoc = l_pSprSaveStart;
|
|
l_rc = STOP_SAVE_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
l_pSprSaveStart++;
|
|
l_saveWordLength--;
|
|
}
|
|
|
|
return l_rc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* @brief searches a self save entry of an SPR in self-save segment.
|
|
* @param[in] i_pSaveReg start of editable location of a SPR save entry.
|
|
* @param[in] i_sprNum Id of the SPR for which entry needs to be edited.
|
|
* @return STOP_SAVE_SUCCESS if look up succeeds, error code otherwise.
|
|
*/
|
|
STATIC StopReturnCode_t updateSelfSaveEntry( uint32_t* i_pSaveReg, uint16_t i_sprNum )
|
|
{
|
|
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
|
|
|
|
do
|
|
{
|
|
if( !i_pSaveReg )
|
|
{
|
|
l_rc = STOP_SAVE_FAIL;
|
|
MY_ERR( "Failed to update self save area for SPR 0x%04x", i_sprNum );
|
|
break;
|
|
}
|
|
|
|
if( P9_STOP_SPR_MSR == i_sprNum )
|
|
{
|
|
*i_pSaveReg = getMfmsrInstruction( 1 );
|
|
}
|
|
else
|
|
{
|
|
*i_pSaveReg = getMfsprInstruction( 1, i_sprNum );
|
|
}
|
|
|
|
i_pSaveReg++;
|
|
|
|
*i_pSaveReg = getBranchLinkRegInstruction( );
|
|
}
|
|
while(0);
|
|
|
|
return l_rc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
StopReturnCode_t p9_stop_save_cpureg_control( void* i_pImage,
|
|
const uint64_t i_pir,
|
|
const uint32_t i_saveRegVector )
|
|
{
|
|
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
|
|
uint32_t l_coreId = 0;
|
|
uint32_t l_threadId = 0;
|
|
uint32_t l_sprPos = 0;
|
|
uint32_t l_sprIndex = 0;
|
|
uint32_t l_lookupLength = 0;
|
|
uint32_t l_lookUpKey = 0;
|
|
uint32_t* l_pSaveStart = NULL;
|
|
uint32_t* l_pRestoreStart = NULL;
|
|
uint32_t* l_pSprSave = NULL;
|
|
void* l_pTempLoc = NULL;
|
|
SmfHomerSection_t* l_pHomer = NULL;
|
|
uint8_t l_selfRestVer = 0;
|
|
|
|
do
|
|
{
|
|
l_rc = getCoreAndThread( i_pImage, i_pir, &l_coreId, &l_threadId );
|
|
|
|
if( l_rc )
|
|
{
|
|
MY_ERR( "Error in getting core no 0x%08x and thread no 0x%08x from PIR 0x%016lx",
|
|
l_coreId, l_threadId, i_pir );
|
|
break;
|
|
}
|
|
|
|
l_rc = validateArgumentSaveRegMask( i_pImage, l_coreId, l_threadId, i_saveRegVector );
|
|
|
|
if( l_rc )
|
|
{
|
|
MY_ERR( "Invalid argument rc 0x%08x", (uint32_t) l_rc );
|
|
break;
|
|
}
|
|
|
|
l_pHomer = ( SmfHomerSection_t * )i_pImage;
|
|
l_selfRestVer = *(uint8_t *)((uint8_t *)i_pImage + CPMR_HOMER_OFFSET + CPMR_SELF_RESTORE_VER_BYTE );
|
|
|
|
for( l_sprIndex = 0; l_sprIndex < MAX_SPR_SUPPORTED; l_sprIndex++ )
|
|
{
|
|
l_sprPos = g_sprRegister[l_sprIndex].iv_saveMaskPos;
|
|
|
|
//Check if a given SPR needs to be self-saved each time on STOP entry
|
|
|
|
if( i_saveRegVector & ( TEST_BIT_PATTERN >> l_sprPos ) )
|
|
{
|
|
|
|
if( g_sprRegister[l_sprIndex].iv_isThreadScope )
|
|
{
|
|
l_lookupLength = SMF_SELF_SAVE_THREAD_AREA_SIZE;
|
|
l_pSaveStart =
|
|
(uint32_t*)&l_pHomer->iv_coreThreadRestore[l_coreId].iv_threadSaveArea[l_threadId][0];
|
|
l_pRestoreStart =
|
|
(uint32_t*)&l_pHomer->iv_coreThreadRestore[l_coreId].iv_threadRestoreArea[l_threadId][0];
|
|
}
|
|
else
|
|
{
|
|
l_lookupLength = SMF_CORE_SAVE_CORE_AREA_SIZE;
|
|
l_pSaveStart = (uint32_t*)&l_pHomer->iv_coreThreadRestore[l_coreId].iv_coreSaveArea[0];
|
|
l_pRestoreStart = (uint32_t*)&l_pHomer->iv_coreThreadRestore[l_coreId].iv_coreRestoreArea[0];
|
|
}
|
|
|
|
// an SPR restore section for given core already exists
|
|
l_lookUpKey = genKeyForSprLookup( ( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId );
|
|
|
|
l_rc = lookUpSprInImage( (uint32_t*)l_pRestoreStart, l_lookUpKey,
|
|
g_sprRegister[l_sprIndex].iv_isThreadScope, &l_pTempLoc,
|
|
l_selfRestVer );
|
|
|
|
if( l_rc )
|
|
{
|
|
//SPR specified in the save mask but there is no restore entry present in the memory
|
|
//Self-Save instruction will edit it during STOP entry to make it a valid entry
|
|
|
|
l_rc = p9_stop_save_cpureg( i_pImage,
|
|
(CpuReg_t)g_sprRegister[l_sprIndex].iv_sprId,
|
|
0x00, //creates a dummy entry
|
|
i_pir );
|
|
}
|
|
|
|
//Find if SPR-Save eye catcher exist in self-save segment of SPR restore region.
|
|
l_rc = lookUpSelfSaveSpr( l_sprPos, l_pSaveStart, l_lookupLength, &l_pSprSave );
|
|
|
|
if( l_rc )
|
|
{
|
|
MY_INF( "Failed to find SPR No %02d save entry", l_sprPos );
|
|
l_rc = STOP_SAVE_SPR_ENTRY_MISSING;
|
|
break;
|
|
}
|
|
|
|
l_pSprSave++; //point to next instruction location
|
|
|
|
//update specific instructions of self save region to enable saving for SPR
|
|
l_rc = updateSelfSaveEntry( l_pSprSave, g_sprRegister[l_sprIndex].iv_sprId );
|
|
|
|
}// end if( i_saveRegVector..)
|
|
}// end for
|
|
}
|
|
while(0);
|
|
|
|
return l_rc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------
|
|
|
|
StopReturnCode_t p9_stop_init_cpureg( void* const i_pImage, const uint32_t i_corePos )
|
|
{
|
|
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
|
|
uint32_t* l_pRestoreStart = NULL;
|
|
void* l_pTempLoc = NULL;
|
|
SmfHomerSection_t* l_pHomer = NULL;
|
|
uint32_t l_threadPos = 0;
|
|
uint32_t l_lookUpKey = 0;
|
|
uint32_t l_sprIndex = 0;
|
|
uint8_t l_selfRestVer = 0;
|
|
|
|
MY_INF( ">> p9_stop_init_cpureg" );
|
|
|
|
do
|
|
{
|
|
if( !i_pImage )
|
|
{
|
|
l_rc = STOP_SAVE_ARG_INVALID_IMG;
|
|
break;
|
|
}
|
|
|
|
if( i_corePos > MAX_CORE_ID_SUPPORTED )
|
|
{
|
|
l_rc = STOP_SAVE_ARG_INVALID_CORE;
|
|
break;
|
|
}
|
|
|
|
l_pHomer = ( SmfHomerSection_t * ) i_pImage;
|
|
l_selfRestVer = *(uint8_t *)((uint8_t *)i_pImage + CPMR_HOMER_OFFSET + CPMR_SELF_RESTORE_VER_BYTE );
|
|
|
|
for( l_sprIndex = 0; l_sprIndex < MAX_SPR_SUPPORTED; l_sprIndex++ )
|
|
{
|
|
//Check if a given SPR needs to be self-saved each time on STOP entry
|
|
|
|
l_lookUpKey = genKeyForSprLookup( ( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId );
|
|
|
|
if( g_sprRegister[l_sprIndex].iv_isThreadScope )
|
|
{
|
|
for( l_threadPos = 0; l_threadPos < MAX_THREADS_PER_CORE; l_threadPos++ )
|
|
{
|
|
l_pRestoreStart =
|
|
(uint32_t*)&l_pHomer->iv_coreThreadRestore[i_corePos].iv_threadRestoreArea[l_threadPos][0];
|
|
|
|
l_rc = lookUpSprInImage( (uint32_t*)l_pRestoreStart, l_lookUpKey,
|
|
g_sprRegister[l_sprIndex].iv_isThreadScope,
|
|
&l_pTempLoc,
|
|
l_selfRestVer );
|
|
|
|
if( l_rc )
|
|
{
|
|
MY_ERR( "Thread SPR lookup failed in p9_stop_init_cpureg SPR %d Core %d Thread %d Index %d",
|
|
g_sprRegister[l_sprIndex].iv_sprId, i_corePos, l_threadPos, l_sprIndex );
|
|
break;
|
|
}
|
|
|
|
l_rc = updateSprEntryInImage( (uint32_t*) l_pTempLoc,
|
|
( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId,
|
|
0x00,
|
|
INIT_SPR_REGION );
|
|
|
|
if( l_rc )
|
|
{
|
|
MY_ERR( "Thread SPR region init failed. Core %d SPR Id %d",
|
|
i_corePos, g_sprRegister[l_sprIndex].iv_sprId );
|
|
break;
|
|
}
|
|
|
|
}//end for thread
|
|
|
|
if( l_rc )
|
|
{
|
|
break;
|
|
}
|
|
|
|
}//end if SPR threadscope
|
|
else
|
|
{
|
|
l_pRestoreStart = (uint32_t*)&l_pHomer->iv_coreThreadRestore[i_corePos].iv_coreRestoreArea[0];
|
|
|
|
l_rc = lookUpSprInImage( (uint32_t*)l_pRestoreStart, l_lookUpKey,
|
|
g_sprRegister[l_sprIndex].iv_isThreadScope,
|
|
&l_pTempLoc, l_selfRestVer );
|
|
|
|
if( l_rc )
|
|
{
|
|
MY_ERR( "Core SPR lookup failed in p9_stop_init_cpureg" );
|
|
break;
|
|
}
|
|
|
|
l_rc = updateSprEntryInImage( (uint32_t*) l_pTempLoc,
|
|
( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId,
|
|
0x00,
|
|
INIT_SPR_REGION );
|
|
|
|
if( l_rc )
|
|
{
|
|
MY_ERR( "Core SPR region init failed. Core %d SPR Id %d SPR Index %d",
|
|
i_corePos, g_sprRegister[l_sprIndex].iv_sprId, l_sprIndex );
|
|
break;
|
|
}
|
|
|
|
}// end else
|
|
|
|
}// end for l_sprIndex
|
|
|
|
}
|
|
while(0);
|
|
|
|
MY_INF( "<< p9_stop_init_cpureg" );
|
|
return l_rc;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------
|
|
|
|
StopReturnCode_t p9_stop_init_self_save( void* const i_pImage, const uint32_t i_corePos )
|
|
{
|
|
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
|
|
uint32_t* l_pSaveStart = NULL;
|
|
SmfHomerSection_t * l_pHomer = NULL;
|
|
uint32_t l_threadPos = 0;
|
|
uint32_t l_sprBitPos = 0;
|
|
uint32_t l_sprIndexAdj = 0;
|
|
MY_INF( ">> p9_stop_init_self_save" );
|
|
|
|
do
|
|
{
|
|
if( !i_pImage )
|
|
{
|
|
l_rc = STOP_SAVE_ARG_INVALID_IMG;
|
|
break;
|
|
}
|
|
|
|
if( i_corePos > MAX_CORE_ID_SUPPORTED )
|
|
{
|
|
l_rc = STOP_SAVE_ARG_INVALID_CORE;
|
|
break;
|
|
}
|
|
|
|
l_pHomer = ( SmfHomerSection_t*) i_pImage;
|
|
|
|
for( l_threadPos = 0; l_threadPos < MAX_THREADS_PER_CORE; l_threadPos++ )
|
|
{
|
|
l_pSaveStart =
|
|
(uint32_t*)&l_pHomer->iv_coreThreadRestore[i_corePos].iv_threadSaveArea[l_threadPos][0];
|
|
|
|
//Adding instruction 'mflr r30'
|
|
*l_pSaveStart = SWIZZLE_4_BYTE(MFLR_R30);
|
|
l_pSaveStart++;
|
|
|
|
for( l_sprBitPos = 0; l_sprBitPos <= MAX_SPR_BIT_POS; l_sprBitPos++ )
|
|
{
|
|
l_rc = getSprRegIndexAdjustment( l_sprBitPos, &l_sprIndexAdj );
|
|
|
|
if( STOP_SAVE_SPR_BIT_POS_RESERVE == l_rc )
|
|
{
|
|
//Failed to find SPR index adjustment
|
|
continue;
|
|
}
|
|
|
|
if( !g_sprRegister[l_sprBitPos - l_sprIndexAdj].iv_isThreadScope )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Initialize self save region with SPR save entry for each thread
|
|
//level SPR
|
|
l_rc = initSelfSaveEntry( l_pSaveStart,
|
|
g_sprRegister[l_sprBitPos - l_sprIndexAdj].iv_saveMaskPos );
|
|
|
|
if( l_rc )
|
|
{
|
|
MY_ERR( "Failed to init thread self-save region for core %d thread %d",
|
|
i_corePos, l_threadPos );
|
|
break;
|
|
}
|
|
|
|
l_pSaveStart++;
|
|
l_pSaveStart++;
|
|
l_pSaveStart++;
|
|
}
|
|
|
|
}// for thread = 0;
|
|
|
|
if( l_rc )
|
|
{
|
|
//breakout if saw an error while init of thread SPR region
|
|
break;
|
|
}
|
|
|
|
l_pSaveStart =
|
|
(uint32_t*)&l_pHomer->iv_coreThreadRestore[i_corePos].iv_coreSaveArea[0];
|
|
|
|
*l_pSaveStart = SWIZZLE_4_BYTE(MFLR_R30);
|
|
l_pSaveStart++;
|
|
|
|
for( l_sprBitPos = 0; l_sprBitPos <= MAX_SPR_BIT_POS; l_sprBitPos++ )
|
|
{
|
|
l_rc = getSprRegIndexAdjustment( l_sprBitPos, &l_sprIndexAdj );
|
|
|
|
if( STOP_SAVE_SPR_BIT_POS_RESERVE == l_rc )
|
|
{
|
|
//Failed to find SPR index adjustment
|
|
continue;
|
|
}
|
|
|
|
if( g_sprRegister[l_sprBitPos - l_sprIndexAdj].iv_isThreadScope )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//Initialize self save region with SPR save entry for each core
|
|
//level SPR
|
|
l_rc = initSelfSaveEntry( l_pSaveStart,
|
|
g_sprRegister[l_sprBitPos - l_sprIndexAdj].iv_saveMaskPos );
|
|
|
|
if( l_rc )
|
|
{
|
|
MY_ERR( "Failed to init core self-save region for core %d thread %d",
|
|
i_corePos, l_threadPos );
|
|
break;
|
|
}
|
|
|
|
l_pSaveStart++;
|
|
l_pSaveStart++;
|
|
l_pSaveStart++;
|
|
}
|
|
}
|
|
while(0);
|
|
|
|
MY_INF( "<< p9_stop_init_self_save" );
|
|
return l_rc;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
} //namespace stopImageSection ends
|
|
|
|
} //extern "C"
|
|
#endif
|