/****************************************************************************** | |
* | |
* (C)Copyright 2022 ASR. All Rights Reserved. | |
* | |
******************************************************************************/ | |
#include "GEU_Provisioning.h" | |
#include "Security.h" | |
#include "Errors.h" | |
#include "platform_geu_fuse_internal.h" | |
#include "geu_interface.h" | |
#include "GEU.h" | |
#include "ProtocolManager.h" | |
#include "timer.h" | |
#include "kstr_fuse_config.h" | |
/** | |
* get fuse configuration spec | |
*/ | |
// FixME: use generic fuse_conf_spec | |
static fuse_conf_spec_t GetFuseConfSpec(void* parameter) | |
{ | |
return kstr_fuse_conf_spec; | |
} | |
/** | |
* Attention: this function must kept consistent with what is done by Boot ROM, | |
* for exmaple, the pack order for public key | |
* perform platform binding | |
* @parameter ptim: pointer to TIM | |
* @parameter parameter: should be NULL | |
* @return: FUSE_FuseBurnPowerNotEnabled if UsbPhy band gap is not enabled | |
* other error code otherwise | |
* | |
* Note: Due to the need of provisioning of non-trusted platform and minimize | |
* the code size, we have a dummy implementation of this function for | |
* non-trusted case. | |
*/ | |
// FixME: set algorithm id using info form TIM | |
extern UINT_T GEU_LIB_BindPlatform(pTIM ptim, void* parameter) | |
{ | |
UINT_T result = NoError; | |
UINT_T key_word_cnt, hash_data_size_in_bytes; | |
UINT_T data[BINDKEYSIZE] = {0}; | |
UINT_T fusedata[DIGEST_WLEN_MAX]; | |
UINT_T digest[DIGEST_WLEN_MAX]; | |
struct GEU_FuseBurnStatus burn_status; | |
struct GEU_FuseBurnStatus* pburn_status = &burn_status; | |
UINT_T programmed = 0; | |
UINT_T i; | |
pSECURITY_FUNCTIONS pSFs = GetSecurityFunctionsPointer(); | |
GEU_UARTLOG("S OEMKeyHash...\n\r"); | |
result = GEU_ReadOemHashKeyFuseBits(fusedata, K_SHA256_SIZE); | |
if (result != NoError) { | |
return result; | |
} | |
for (i = 0; i < K_SHA256_SIZE >> 2; i++) { | |
if (fusedata[i] != 0) { | |
programmed = 1; | |
break; | |
} | |
} | |
if (ptim->pConsTIM->VersionBind.Version < (TIM_3_4_00)) { | |
/* do not support TIM_3_3_00 and TIM_3_2_00 anymore */ | |
return InvalidTIMVersionError; | |
} | |
/* compute platform verification key digest */ | |
/* after TIM version 3.2.00 all key size is in bit */ | |
key_word_cnt = ptim->pTBTIM_DS->KeySize / 32; | |
hash_data_size_in_bytes = (key_word_cnt*2 + 1) * 4; // (crypto_scheme||modulus-or-X||exponent-or-Y) in bytes (*4) | |
// 1) Pack the crypto_scheme | |
//crypto_scheme: 0x0000A100 PKCSv1_SHA1_1024RSA | |
// 0x0000A110 PKCSv1_SHA256_1024RSA | |
// 0x0000A200 PKCSv1_SHA1_2048RSA | |
// 0x0000A210 PKCSv1_SHA256_2048RSA | |
if(ptim->pTBTIM_DS->HashAlgorithmID == SHA256) | |
data[0] = (key_word_cnt == 64) ? PKCSv1_SHA256_2048RSA : PKCSv1_SHA256_1024RSA; | |
else // Defaults to 160 | |
data[0] = (key_word_cnt == 64) ? PKCSv1_SHA1_2048RSA : PKCSv1_SHA1_1024RSA; | |
// 2) Pack the Modulus and Exponent | |
for (i=0; i < key_word_cnt; i++) | |
{ | |
data[i+1] = ptim->pTBTIM_DS->Rsa.RSAModulus[i]; | |
data[i+key_word_cnt+1] = ptim->pTBTIM_DS->Rsa.RSAPublicExponent[i]; | |
} | |
//Key Padding is on by default | |
hash_data_size_in_bytes = BINDKEYSIZE*4; // MaxKeySize*2+CryptoScheme in Bytes: 129*4 = 516 | |
/* compute digest */ | |
for (i = 0; i < DIGEST_WLEN_MAX; i++) { | |
digest[i] = 0; | |
} | |
result = pSFs->pSHAMessageDigest((UINT8_T *)data, hash_data_size_in_bytes, | |
(UINT8_T *)digest, ptim->pTBTIM_DS->HashAlgorithmID); | |
if (result != NoError) { | |
return result; | |
} | |
/* allow reprogramming as it is often the case during development */ | |
if (programmed) { | |
for (i = 0; i < K_SHA256_SIZE >> 2; i++) { | |
if (fusedata[i] != digest[i]) { | |
return FUSE_BurnError; | |
} | |
} | |
return NoError; | |
} | |
/* make sure power supply required for fuse programming is enabled */ | |
result = GEU_EnableFuseBurnPower(); | |
if (result != NoError) { | |
return FUSE_FuseBurnPowerNotEnabled; | |
} | |
/* go burn the platform key hash */ | |
result = GEU_SetActiveFuseBlock(K_OEM_FUSEBLOCK); | |
if (result != NoError) { | |
return result; | |
} | |
result = GEU_SetupOemHashKeyFuseBits(digest, 4 * DIGEST_WLEN_MAX); | |
if (result != NoError) { | |
return result; | |
} | |
result = GEU_BurnFuseBlock_OemHashKey(pburn_status); | |
if(pburn_status->FinalBurnStatus) { | |
GEU_UARTLOG("Burn status: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n\r", | |
pburn_status->FinalBurnStatus, | |
pburn_status->CorrectedBurnStatus, | |
pburn_status->RawBurnStatus, | |
pburn_status->SavedBurnRequest, | |
pburn_status->DebugStatus); | |
} | |
GEU_UARTLOG("E OEMKeyHash.\n\r"); | |
return result; | |
} | |
// This function is called from OBM | |
// Always returns NoError for GEU | |
extern UINT_T GEU_LIB_VerifyPlatform(pTIM ptim) | |
{ | |
UINT_T result; | |
UINT_T key_word_cnt, hash_data_size_in_bytes; | |
UINT_T data[BINDKEYSIZE]= {0}; | |
UINT_T fusedata[DIGEST_WLEN_MAX]; | |
UINT_T digest[DIGEST_WLEN_MAX]; | |
UINT_T programmed = 0; | |
UINT_T i; | |
pSECURITY_FUNCTIONS pSFs = GetSecurityFunctionsPointer(); | |
result = GEU_ReadOemHashKeyFuseBits(fusedata, K_SHA256_SIZE); | |
if (result != NoError) { | |
return result; | |
} | |
for (i = 0; i < K_SHA256_SIZE >> 2; i++) { | |
if (fusedata[i] != 0) { | |
programmed = 1; | |
break; | |
} | |
} | |
if (ptim->pConsTIM->VersionBind.Version < (TIM_3_4_00)) { | |
/* do not support TIM_3_3_00 and TIM_3_2_00 anymore */ | |
return InvalidTIMVersionError; | |
} | |
/* after TIM version 3.2.00 all key size is in bit */ | |
key_word_cnt = ptim->pTBTIM_DS->KeySize / 32; | |
hash_data_size_in_bytes = (key_word_cnt*2 + 1) * 4; // (crypto_scheme||modulus-or-X||exponent-or-Y) in bytes (*4) | |
// 1) Pack the crypto_scheme | |
//crypto_scheme: 0x0000A100 PKCSv1_SHA1_1024RSA | |
// 0x0000A110 PKCSv1_SHA256_1024RSA | |
// 0x0000A200 PKCSv1_SHA1_2048RSA | |
// 0x0000A210 PKCSv1_SHA256_2048RSA | |
if(ptim->pTBTIM_DS->HashAlgorithmID == SHA256) | |
data[0] = (key_word_cnt == 64) ? PKCSv1_SHA256_2048RSA : PKCSv1_SHA256_1024RSA; | |
else // Defaults to 160 | |
data[0] = (key_word_cnt == 64) ? PKCSv1_SHA1_2048RSA : PKCSv1_SHA1_1024RSA; | |
// 2) Pack the Modulus and Exponent | |
for (i=0; i < key_word_cnt; i++) | |
{ | |
data[i+1] = ptim->pTBTIM_DS->Rsa.RSAModulus[i]; | |
data[i+key_word_cnt+1] = ptim->pTBTIM_DS->Rsa.RSAPublicExponent[i]; | |
} | |
//Key Padding is on by default | |
hash_data_size_in_bytes = BINDKEYSIZE*4; // MaxKeySize*2+CryptoScheme in Bytes: 129*4 = 516 | |
/* compute digest */ | |
for (i = 0; i < DIGEST_WLEN_MAX; i++) { | |
digest[i] = 0; | |
} | |
result = pSFs->pSHAMessageDigest((UINT8_T *)data, hash_data_size_in_bytes, | |
(UINT8_T *)digest, ptim->pTBTIM_DS->HashAlgorithmID); | |
if (result != NoError) { | |
return result; | |
} | |
/* compare fuses against TIM */ | |
if (programmed) { | |
for (i = 0; i < K_SHA256_SIZE >> 2; i++) { | |
if (fusedata[i] != digest[i]) { | |
return FUSE_FuseBlockCompareFailed; | |
} | |
} | |
} | |
return NoError; | |
} | |
/** | |
* Not Implemented for KSTR | |
* @return: always NoError | |
*/ | |
extern UINT_T GEU_LIB_BindJTAGKey(pTIM ptim, void* parameter) | |
{ | |
return NoError; | |
} | |
/** | |
* Not Implemented for KSTR | |
* @return: always NoError | |
*/ | |
extern UINT_T GEU_LIB_VerifyJTAGKey(pTIM ptim) | |
{ | |
return NoError; | |
} | |
/** | |
* perform platform auto-configuration | |
* @parameter ptim: pointer to TIM | |
* @parameter parameter: NULL | |
*/ | |
UINT_T GEU_LIB_AutoConfigPlatform(pTIM ptim, void* parameter) | |
{ | |
UINT_T result = NoError; | |
fuse_conf_spec_t fuse_conf; | |
struct GEU_FuseBurnStatus burn_status; | |
struct GEU_FuseBurnStatus* pburn_status = &burn_status; | |
fuse_conf = GetFuseConfSpec(NULL); | |
/* make sure power supply required for fuse programming is enabled */ | |
result = GEU_EnableFuseBurnPower(); | |
if (result != NoError) { | |
return FUSE_FuseBurnPowerNotEnabled; | |
} | |
GEU_UARTLOG("S SoC Config...\n\r"); | |
/* | |
* configure SoC Configs | |
*/ | |
result = GEU_LIB_ProgramSocConfig(NULL, ptim); | |
if (result != NoError) { | |
return result; | |
} | |
result = GEU_BurnFuseBlock_SocConfig(pburn_status); | |
if(pburn_status->FinalBurnStatus) { | |
GEU_UARTLOG("Burn status: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n\r", | |
pburn_status->FinalBurnStatus, | |
pburn_status->CorrectedBurnStatus, | |
pburn_status->RawBurnStatus, | |
pburn_status->SavedBurnRequest, | |
pburn_status->DebugStatus); | |
} | |
GEU_UARTLOG("E SoC Config...\n\r"); | |
return result; | |
} | |
/** | |
* program SoC config fuse bits | |
*/ | |
UINT_T GEU_LIB_ProgramSocConfig(void* parameter, pTIM ptim) | |
{ | |
UINT_T result, flash_number; | |
fuse_conf_spec_t fuse_conf; | |
fuse_conf = GetFuseConfSpec(NULL); | |
result = GEU_SetActiveFuseBlock(K_SOC_CONFIG_FUSEBLOCK); | |
if (result != NoError) { | |
return result; | |
} | |
if (ptim != NULL) { | |
if (ptim->pConsTIM->VersionBind.Trusted) | |
fuse_conf.plat_ap_conf[2] |= (OPMODETRUSTED); // Trusted | |
else | |
fuse_conf.plat_ap_conf[2] |= (OPMODENONTRUSTED); // Non-Trusted | |
} | |
result = GEU_SetupApConfigFuseBits(&fuse_conf.plat_ap_conf[0], fuse_conf.ap_bit_cnt/8); | |
if( result ) | |
GEU_UARTLOG("Setup AP Config Error: 0x%x\n\r", result); | |
result = GEU_SetupTopConfigFuseBits(&fuse_conf.plat_top_conf[0], fuse_conf.top_bit_cnt/8); | |
if( result ) | |
GEU_UARTLOG("Setup TOP Config Error: 0x%x\n\r", result); | |
return result; | |
} | |
UINT_T GEU_ProgramMajorVersion(pTIM ptim) | |
{ | |
UINT_T result; | |
UINT_T MajorVersion, PlatVersion; | |
struct GEU_FuseBurnStatus burn_status; | |
struct GEU_FuseBurnStatus* pburn_status = &burn_status; | |
result = GEU_SetActiveFuseBlock(K_PLAT_VERSION_FUSEBLOCK); | |
if (result != NoError) { | |
return result; | |
} | |
result = CheckAntiRollbackPackage(ptim, &MajorVersion); | |
if(result & MAJOR_ATRB_ENABLED == 0) /* Anti-rollback disabled */ | |
{ | |
GEU_UARTLOG("Get Major version error: %d\n\r", result); | |
return FUSE_InvalidRequest; | |
} | |
if(MajorVersion > 31) | |
{ | |
GEU_UARTLOG("MajorVersion exceed valid range: %d\n\r", MajorVersion); | |
return NoError; | |
} | |
/* Bit0 means anti-rollback is enabled | |
*/ | |
PlatVersion = (1 << MajorVersion ) | 1; | |
GEU_SetupPlatVersionFuseBits(&PlatVersion, K_PLAT_VERSION_FUSE_SIZE); | |
result = GEU_BurnFuseBlock_SocConfig(pburn_status); | |
if(pburn_status->FinalBurnStatus) { | |
GEU_UARTLOG("Burn status: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n\r", | |
pburn_status->FinalBurnStatus, | |
pburn_status->CorrectedBurnStatus, | |
pburn_status->RawBurnStatus, | |
pburn_status->SavedBurnRequest, | |
pburn_status->DebugStatus); | |
} | |
return result; | |
} | |
UINT_T GEU_ProgramMiscConfig(pTIM pTIM_h) | |
{ | |
UINT_T Retval = NoError; | |
UINT_T ApConfig[3]; | |
UINT_T ApConfigBurned[3]; | |
UINT_T TopConfig = 0; | |
UINT_T TopConfigBurned = 0; | |
UINT_T LifeCyle = 0; | |
UINT_T LifeCyleRead = 0; | |
UINT_T HwLock = 0; | |
UINT_T HwLockRead = 0; | |
UINT_T SoCBurnRequest = 0; /* Block 0 */ | |
UINT_T MiscBurnRequest = 0; /* Block 6 */ | |
pImage_FUSE_BURN pFuseBurn; | |
pWTP_RESERVED_AREA_HEADER pWRAH = NULL; | |
UINT_T FuseBurnCfg = 0; | |
struct GEU_FuseBurnStatus burn_status; | |
struct GEU_FuseBurnStatus* pburn_status = &burn_status; | |
memset(ApConfig, 0, sizeof(ApConfig)); | |
memset(ApConfigBurned, 0, sizeof(ApConfigBurned)); | |
pWRAH = FindPackageInReserved(&Retval, pTIM_h, FUSE); | |
if ( !( (pWRAH == NULL) || (Retval != NoError) ) ) | |
{ | |
pFuseBurn = (pImage_FUSE_BURN) pWRAH; | |
FuseBurnCfg = pFuseBurn->Enabled; | |
FuseBurnCfg &= ~TRUSTBOOT_BURN_EN; | |
} | |
if(FuseBurnCfg == 0) | |
return NoError; | |
#if CLOSE_ESCAPE_SEQ || CLOSE_ROM_LOG || CLOSE_ROM_USB | |
GEU_ReadApConfigFuseBits(ApConfigBurned, K_AP_CONFIG_FUSE_SIZE); | |
if(FuseBurnCfg & DISESCAPE_BURN_EN) | |
ApConfig[2] |= DIS_ESCAPE_SEQ; | |
if(FuseBurnCfg & DISROMLOG_BURN_EN) | |
ApConfig[2] |= UART_LOG_DISABLE; | |
if(FuseBurnCfg & CLOSEUSBD_BURN_EN) | |
ApConfig[2] |= USB_DOWNLOAD_DISABLE; | |
if( ApConfig[2] && ((ApConfig[2] & ApConfigBurned[2]) != ApConfig[2]) ) | |
SoCBurnRequest |= K_AP_CONFIG_BURN_REQUEST_MASK; | |
#endif | |
#if ENABLE_LIFTCYCLE | |
if(FuseBurnCfg & LIFECYLE_CM_BURN_EN) | |
LifeCyle = LCS_CM; | |
if(FuseBurnCfg & LIFECYLE_DM_BURN_EN) | |
LifeCyle = LCS_DM; | |
if(FuseBurnCfg & LIFECYLE_SP_BURN_EN) | |
LifeCyle = LCS_SP; | |
if(FuseBurnCfg & LIFECYLE_RMA_BURN_EN) | |
LifeCyle = LCS_RMA; | |
LifeCyleRead = GEU_ReadLifeCycleState(); | |
if(LifeCyle > LifeCyleRead) | |
MiscBurnRequest |= K_LIFECYCLE_BURN_REQUEST_MASK; | |
#endif | |
#if ENABLE_HWLOCK | |
if(FuseBurnCfg & BANKHWLOCK_BURN_EN) { | |
HwLock = (FuseBurnCfg & BANKHWLOCK_BURN_EN) >> 16; | |
HwLockRead = GEU_ReadHWLockState(); | |
if(HwLock && ((HwLock & HwLockRead) != HwLock)) | |
MiscBurnRequest |= K_HWLOCK_BURN_REQUEST_MASK; | |
} | |
#endif | |
#if CLOSE_JTAG | |
if(FuseBurnCfg & CLOSEJTAG_BURN_EN) { | |
if(LifeCyle != 0 && LifeCyle != LCS_SP) { | |
obm_printf("Requested LifeCyle mismatch with Jtag disable requirement, abort\n\r"); | |
return NoError; | |
} | |
LifeCyle = LCS_SP; | |
LifeCyleRead = GEU_ReadLifeCycleState(); | |
TopConfig |= JTAG_PORT_DISABLE; | |
if(LifeCyle > LifeCyleRead) { | |
MiscBurnRequest |= K_LIFECYCLE_BURN_REQUEST_MASK; | |
} | |
if(LifeCyle < LifeCyleRead) { | |
obm_printf("Current LifeCyle is: 0x%x, can not disable Jtag\n\r", LifeCyleRead); | |
TopConfig &= ~JTAG_PORT_DISABLE; | |
} | |
GEU_ReadTopConfigFuseBits(&TopConfigBurned, K_TOP_CONFIG_FUSE_SIZE); | |
if(TopConfig && ((TopConfig & TopConfigBurned) != TopConfig)) { | |
SoCBurnRequest |= K_TOP_CONFIG_BURN_REQUEST_MASK; | |
} | |
} | |
#endif | |
if(SoCBurnRequest == 0 && MiscBurnRequest == 0) | |
return NoError; | |
/* make sure power supply required for fuse programming is enabled */ | |
Retval = GEU_EnableFuseBurnPower(); | |
if (Retval != NoError) { | |
return FUSE_FuseBurnPowerNotEnabled; | |
} | |
GEU_UARTLOG("S Misc Config...\n\r"); | |
if(SoCBurnRequest) { | |
Retval = GEU_SetActiveFuseBlock(K_APCPMP0_FUSEBLOCK); | |
if (Retval != NoError) { | |
return Retval; | |
} | |
if(SoCBurnRequest & K_AP_CONFIG_BURN_REQUEST_MASK) { | |
Retval = GEU_SetupApConfigFuseBits(ApConfig, K_AP_CONFIG_FUSE_SIZE); | |
if( Retval ) | |
GEU_UARTLOG("Setup AP Config Error: 0x%x\n\r", Retval); | |
} | |
if(SoCBurnRequest & K_TOP_CONFIG_BURN_REQUEST_MASK) { | |
Retval = GEU_SetupTopConfigFuseBits(&TopConfig, K_TOP_CONFIG_FUSE_SIZE); | |
if( Retval ) | |
GEU_UARTLOG("Setup TOP Config Error: 0x%x\n\r", Retval); | |
} | |
Retval = GEU_BurnFuseBlock_SocConfig(pburn_status); | |
if(pburn_status->FinalBurnStatus) { | |
GEU_UARTLOG("Bank0 Burn status: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n\r", | |
pburn_status->FinalBurnStatus, | |
pburn_status->CorrectedBurnStatus, | |
pburn_status->RawBurnStatus, | |
pburn_status->SavedBurnRequest, | |
pburn_status->DebugStatus); | |
} | |
} | |
if(MiscBurnRequest) { | |
Retval = GEU_SetActiveFuseBlock(K_HWLOCK_LCS_FUSEBLOCK); | |
if (Retval != NoError) { | |
return Retval; | |
} | |
if(MiscBurnRequest & K_LIFECYCLE_BURN_REQUEST_MASK) { | |
Retval = GEU_SetupLifeCycleFuseBits(&LifeCyle, K_LCS_FUSE_SIZE); | |
if( Retval ) | |
GEU_UARTLOG("Setup LCS Error: 0x%x\n\r", Retval); | |
} | |
if(MiscBurnRequest & K_HWLOCK_BURN_REQUEST_MASK) { | |
Retval = GEU_SetupLifeCycleFuseBits(&LifeCyle, K_LCS_FUSE_SIZE); | |
if( Retval ) | |
GEU_UARTLOG("Setup LCS Error: 0x%x\n\r", Retval); | |
} | |
Retval = GEU_BurnFuseBlock_MISCConfig(pburn_status); | |
if(pburn_status->FinalBurnStatus) { | |
GEU_UARTLOG("Bank6 Burn status: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n\r", | |
pburn_status->FinalBurnStatus, | |
pburn_status->CorrectedBurnStatus, | |
pburn_status->RawBurnStatus, | |
pburn_status->SavedBurnRequest, | |
pburn_status->DebugStatus); | |
} | |
} | |
GEU_UARTLOG("E Misc Config...\n\r"); | |
return Retval; | |
} |