blob: 1edf5bb16ab3f3da266075520fd322b589f236bc [file] [log] [blame]
/******************************************************************************
*
* (C)Copyright 2005 - 2011 Marvell. All Rights Reserved.
*
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MARVELL.
* The copyright notice above does not evidence any actual or intended
* publication of such source code.
* This Module contains Proprietary Information of Marvell and should be
* treated as Confidential.
* The information in this file is provided for the exclusive use of the
* licensees of Marvell.
* Such users have the right to use, modify, and incorporate this code into
* products for purposes authorized by the license agreement provided they
* include this notice and the associated copyright notice with any such
* product.
* The information in this file is provided "AS IS" without warranty.
*
******************************************************************************
**
** FILENAME: OBM.c
**
** PURPOSE: OBM main routine
**
** History: Initial Creation 4/23/06
******************************************************************************/
//////////////////////////////////////////////////////////////////////
// Include the main BootLoader header file
// IMPORTANT:
// DO NOT include anything from here. Include any header that is
// needed in the BootLoader.h file.
//////////////////////////////////////////////////////////////////////
#include "BootLoader.h"
#include "obm2osl.h"
#include "fota.h"
#include "serial.h"
extern UINT_T isDownload;
extern UINT_T time_count_enable;
extern UINT8_T time_count_in_sec;
extern OBM2OSL *pOBM2OSL_h;
extern pTEEC_INFO pAsrTeec;
extern UINT_T Reset;
UINT8_T FlashInitDone = 0;
BOOTING_MODE product_mode_select = FUNCTION_MODE;
#if NZA3 || NZAS
/*
* If the obm size is not 4bytes aligned, it will cause data abort in bootrom
* raw nand/spi-flash read API. To workaround this bug, we put a one-dummy-word
* data section at the end of obm so that its size is always 4bytes aligned.
*/
volatile UINT_T dummy4align __attribute__((section("align_data"))) = 0xdeadbeef;
#endif
//////////////////////////////////////////////////////////////////////
// This is the entry point for the Boot Loader.
//
// Inputs: A pointer to the Transfer Struct that is passed in from BootROM.
// Outputs: NONE. Transfers control to OS Loader.
//
// It mainly functions as following:
//
// 1) Initial Setup
// 2) Validate TIM.
// 3) Perform TIM based setup.
// 4) Determine operating mode.
// a) If Download Path (SW Upgrade or Download)
// i. Download Descriptor File
// ii. Validate TIM
// iii. Download TIM Images
// b) If BOOT Path (Single TIM or Dual TIM boot)
// i. Single TIM Boot
// Configure Flashes and FM
// Load Images
// Validate images
// ii. Dual TIM Boot
// Configure Flashes and FM
// Load 2nd TIM
// Set TIM Pointers
// Validate 2nd TIM
// Load Images
// Validate images
// 5) BL Finalization setup
// 6) Transfer Control
//////////////////////////////////////////////////////////////////////
void BootLoaderMain( P_TRANSFER_STRUCT pTS_h )
{
FUSE_SET fuses; // Fuse information passed in from BootROM
IMAGE_INFO_3_4_0 *pBootImageInfo = NULL;
UINT_T Retval = GeneralError;
UINT_T ErrorCode = NoError; // Error Code variable
OPERATING_MODE_T bootMode = UPGRADESW; // initalized so that coverity doesn't complain
UINT_T LoadAddr, ImageSize, ImageID;
// Transfer struct related parameters
UINT_T startOfDayTimerVal = 0; // pTS_h->SOD_ACCR0;
UINT_T TIM_Address = 0; // pTS_h->data_pairs[0].location;
pTIM pTIM_h = GetTimPointer();
// Initial setup
SetupEnvironment(&TIM_Address, pTS_h, &startOfDayTimerVal, pTIM_h, &fuses);
// Perform TIM based setup
PerformTIMBasedSetup(pTIM_h, &fuses);
#if NZA3 || NZAS
dummy4align = 0;
#endif
// Determine operating mode
bootMode = DetermineOperatingMode(&fuses, pTIM_h);
switch (bootMode) {
// DOWNLOAD PATH
case UPGRADESW:
case DOWNLOAD:
// Download Descriptor File
PlatformReleaseEMMDStatus();
pBootImageInfo = DownloadModeMain( &fuses, pTIM_h, bootMode);
break;
// BOOT PATH
case SINGLE_TIM_BOOT:
case PRODUCT_USB_BOOT:
case PRODUCT_UART_BOOT:
case RECOVERY_MODE_BOOT:
case MENU_MODE_BOOT:
pBootImageInfo = BootModeMain(pTIM_h, bootMode, &fuses);
break;
default:
break;
}
// If there is an error or no image is found for booting, we FAIL.
if ((pBootImageInfo == NULL) && (!isDownload)) // if it is download, no print this error code
{
FatalError(NULLPointer, "BLM", NULL);
}
// Finalization Setup
FinalizeSetup(&fuses, pTIM_h);
#if USE_SERIAL_DEBUG
obm_printf("end...\n\r");
#endif
// Transfer control to the next image.
if ((bootMode == UPGRADESW) || (bootMode == DOWNLOAD))
{
if ( isDownload )
{ /* Pure Download */
if (Reset == 1)
{
memset(pOBM2OSL_h, 0, sizeof(OBM2OSL));
pOBM2OSL_h->booting_mode = OBMNODL;
OBMNODL_RST_FLAG = OBMNODL;
do_wdt_reset(); // reset after download as default
}
ErrorCode = 1;
// Infinite loop!
while(ErrorCode&1)
ErrorCode+=2;// need debugger to step beyond this loop after inspecting input args...
}
/* bootMode could be UPGRADESW or DOWNLOAD for upload or just a USB enum
After upload is finished or time out occurs after usb enum, keep booting to
next image, such as uboot or TOS.
Reboot or dead loop only for successful download.
*/
}
if (pBootImageInfo) {
LoadAddr = pBootImageInfo->LoadAddr;
ImageSize = pBootImageInfo->ImageSize;
ImageID = pBootImageInfo->ImageID;
}
/* Transfer control to next image, uboot or TOS */
obm_printf("Jump to %s at 0x%x (size 0x%x)\n\r",
(ImageID == TZSWIDENTIFIER)?"Trusted OS":"OS-loader",
LoadAddr, ImageSize);
pOBM2OSL_h->quiet = serial_get_quiet();
#if defined(CONFIG_TEE_OS)
/* When TEE_OS is involved, use sw_scratch_reg to pass obm2oslo parameters instead of r11 */
BU_REG_WRITE(OBM2OSLO_PARA_REG, (UINT_T)pOBM2OSL_h);
if (ImageID == TZSWIDENTIFIER)
TransferControl2TOS(LoadAddr, (UINT_T)GetNsEntryLoadAddress(),
(UINT_T)pOBM2OSL_h, (UINT_T)pAsrTeec);
#else
TransferControl(LoadAddr, ImageSize, LoadAddr, (UINT_T)pOBM2OSL_h);
#endif
}
//////////////////////////////////////////////////////////////////////
// This is the Environment Setup function for the Boot Loader.
//
// Inputs: Pointer to the address of TIM, Pointer the Transfer Struct
// coming from the BootROM, Pointer to the Timer value, Pointer to the
// Fuses.
// Outputs: None
//
// It mainly functions as following:
// 1) If IPC read is required, gets the transfer struct from IPC.
// 2) Parses the transfer struct.
// 3) Sets the TIM pointers.
// 4) Initializes the SOD Timer interface.
// 5) Enables default clocks.
// 6) Initializes the fuses using platform settings passed in from BootROM.
// 7) Initializes the Keypad required for SW upgrade.
// 8) Initializes the Platform Message Queue.
// 9) Init Security API.
//////////////////////////////////////////////////////////////////////
void SetupEnvironment(UINT_T *TIM_Address, P_TRANSFER_STRUCT pTS_h, UINT_T *startOfDayTimerVal, TIM *pTIM_h, pFUSE_SET pFuses)
{
UINT_T retval = NoError;
// Enable the clocks for the peripherals that will be used
CheckDefaultClocks();
// Initialize the SOD timer interface
InitSODTimer();
ParseTransferStruct(TIM_Address, pTS_h, pFuses, startOfDayTimerVal);
// serial debug
#if USE_SERIAL_DEBUG
PlatformUARTConfig();
serial_init();
obm_printf("start at 0x%x\n\r", OBM_USE_DDR_ADDR);
obm_printf("%s OBM with SWD >= %s\n\r", PLAT_NAME, HexToSwdObmVersion(SWD_VERSION));
OBM_HeapInit();
obm_printf(OBM_COMPILE_INFO);
#endif
SetTIMPointers((UINT8_T*)*TIM_Address, pTIM_h);
if( pTIM_h->pConsTIM->VersionBind.Identifier != TIMIDENTIFIER )
{
// Set error to indicate no TIM is found and make it a fatal error.
FatalError(TIMNotFound, "SE", 1);
}
// Initialize any fuse information passed in by the BootROM
obm_printf("pFuses->value[0]: 0x%x\n\r", pFuses->value[0]);
obm_printf("pFuses->value[1]: 0x%x\n\r", pFuses->value[1]);
//pFuses->bits.SBE = 1;
obm_printf("pFuses->bits.SBE: %d\n\r", pFuses->bits.SBE);
#if TRUSTED && PRODUCT_BUILD
obm_printf("Trusted OBM with ProductBuild\n\r");
#elif TRUSTED && !PRODUCT_BUILD
obm_printf("Trusted OBM without ProductBuild\n\r");
#else
obm_printf("Non-Trusted OBM\n\r");
#endif
#if ENABLE_MMU
obm_printf("MMU Enabled\n\r");
#else
obm_printf("MMU Disabled\n\r");
#endif
#if I2C
I2CInit();
#endif
// Keypad initialization: Required for detecting software upgrade request from the keypad.
//These two lines of code conflict with SDMMC on MMP3,hence commenting them out
#if UPDATE_USE_GPIO
PlatformGPIOKeyConfig();
#endif
PlatformLateInit();
#if TRUSTED
// Init Security API.
//if (pFuses->bits.SBE)
{
obm_printf("SecurityInitialization\n\r");
retval = SecurityInitialization(0);
if(retval != NoError)
{
FatalError(retval, "SE", 2);
}
// Init Provisioning API.
ProvisioningInitialization();
}
#endif
}
//////////////////////////////////////////////////////////////////////
// This is the Parse Transfer Struct function for the Boot Loader.
//
// Inputs: Pointer to the address of TIM, Pointer the Transfer Struct
// coming from the BootROM, Pointer to the Timer value, Pointer to the
// Fuses.
// Outputs: NONE.
//
// It mainly functions as following:
// 1) If the passed in Transfer Struct is not NULL, parse it and initialize
// the platform settings, the timer value, and the TIM address.
// 2) Else if it is NULL, look at a fixed location.
//////////////////////////////////////////////////////////////////////
void ParseTransferStruct(UINT_T *TIM_Address, P_TRANSFER_STRUCT pTS_h, pFUSE_SET pFuses, UINT_T *startOfDayTimerVal)
{
UINT_T i, TS_Version;
P_TRANSFER_STRUCT_v2 pTS_v2;
// If the passed in Transfer Struct is not NULL, parse it and initialize
// the platform settings, the timer value, and the TIM address.
if( pTS_h )
{
TS_Version = pTS_h->TransferID;
if (TS_Version == TBR_XFER) // v1 of transfer struct is used by BootRom
{
pFuses->value[0] = (UINT_T) pTS_h->FuseVal;
pFuses->value[1] = 0x0;
*startOfDayTimerVal = pTS_h->SOD_ACCR0;
#if LAPW
ParseBR_ExtraState(pTS_h->ResumeParam[0]);
#endif
// For the case where the TIM is not in TS coming from the IPC,
// TS includes random values. In this case, this loop can take a
// long time. With MAX_NUMBER_OF_DATA_PAIRS, we put a limit on this.
for( i=0; i<pTS_h->num_data_pairs && i<MAX_NUMBER_OF_DATA_PAIRS; i++)
{
if(pTS_h->data_pairs[i].data_id == TIM_DATA )
{
*TIM_Address = pTS_h->data_pairs[i].location;
return;
}
}
}
else // v2 of transfer struct is used by OBM
{
pTS_v2 = (P_TRANSFER_STRUCT_v2) pTS_h; // cast to v2 of the xfer struct
pFuses->value[0] = pTS_v2->FuseVal[0];
pFuses->value[1] = pTS_v2->FuseVal[1];
*startOfDayTimerVal = pTS_v2->SOD_ACCR0;
// For the case where the TIM is not in TS coming from the IPC,
// TS includes random values. In this case, this loop can take a
// long time. With MAX_NUMBER_OF_DATA_PAIRS, we put a limit on this.
for( i=0; i<pTS_v2->num_data_pairs && i<MAX_NUMBER_OF_DATA_PAIRS; i++)
{
if( pTS_v2->data_pairs[i].data_id == TIM_DATA )
{
*TIM_Address = pTS_v2->data_pairs[i].location;
return;
}
}
}
} //end if( pTS_h )
*TIM_Address = 0; // should never be here!
}
//////////////////////////////////////////////////////////////////////
// This is the Fatal Error function for the Boot Loader.
//
// Inputs: Error code,cause, sub code.
// Outputs: NONE.
//
// It mainly stays here in an infinite loop.
//////////////////////////////////////////////////////////////////////
void FatalError(UINT_T ErrorCode, const CHAR* ErrStr, UINT_T ErrSubCode)
{
UINT_T odd = 1;
// Print out why we failed if it is not a port error.
if (ErrorCode != DownloadPortError)
{
AddMessageError(REPORT_ERROR, ErrorCode);
}
#if USE_SERIAL_DEBUG
err_msg("Fatal Error Code: 0x%x\n\r", ErrorCode);
if (ErrStr != NULL)
{
err_msg("Error cause: %s\n\r", ErrStr);
}
if (ErrSubCode != NULL)
err_msg("Error sub code: 0x%x\n\r", ErrSubCode);
#endif
//if( !isDownload ) // no reset for downloading
// do_wdt_reset();
#if SPINOR_CODE
SPINOR_Disable4BytesMode();
#endif
// Infinite loop!
while(odd&1)
{
// need debugger to step beyond this loop after inspecting input args...
odd+=2;
}
return;
}
//////////////////////////////////////////////////////////////////////
// This is the function that performs TIM based setup in the Boot Loader.
//
// Inputs: A pointer to the TIM and a pointer to the Fuses.
// Outputs: NONE.
//
// It mainly functions as following:
// 1) Configures the operating mode.
// 2) Configures the DDR.
// 3) Enables the interrupts.
// 4) Enables the global interrupts.
// 5) Checks the TIM for port over-writes. If this fails, initializes
// the default ports.
//////////////////////////////////////////////////////////////////////
void PerformTIMBasedSetup(pTIM pTIM_h, pFUSE_SET pFuses)
{
UINT_T Retval = NoError;
#if REPORT_DDR_INFO
MCK5_Check_LPDDR2_Info(pTIM_h);
#endif
// Configure DDR if BootROM doesn't configure it.
if (pFuses->bits.DDRInitialized == FALSE)
{
Retval = CheckAndConfigureDDR(pTIM_h, pFuses);
if (Retval != NoError)
{
FatalError(Retval, "PTBS", NULL);
}
}
// Interrupts may be required, so enable them.
// For example, downloading over USB requires interrupts.
// point the interrupt exception handler to our routine.
// this allows us to run in-place out of isram
// without having to enable the enable the mmu.
SetInterruptVector(VECTOR_SRAM_BASE_ADDR);
IRQ_Glb_Dis(); // turns off all interrupts...
EnableIrqInterrupts(); // Enable Interrupts at the core
IRQ_Glb_Ena(); // Enable Global Ints using the AP Global Interrupt Mask Register
//if (NoFrequencyChange == 0) // change pp as default
// pp_set();
#if CP_BOOT
/* ASR1803S boot to MSA L2SRAM */
#if FLCN || FACT
BU_REG_WRITE(0xD4050020, 0x23);
SetupL2SRAMMPU();
#endif
#endif
}
static INT FindDkbiPatternInTIM(pTIM pTIM_h)
{
pIMAGE_INFO_3_4_0 pImgInfo = NULL;
UINT_T TimSize;
UINT_T DkbiPatternAddr;
pImgInfo = FindImageInTIM(pTIM_h, TIMIDENTIFIER);
if(pImgInfo == NULL){
return 0;
}
TimSize = (pImgInfo->ImageSize + 3) & (~3); //4byte align
DkbiPatternAddr = 0xD1000000 + TimSize;
if(BU_REG_READ(DkbiPatternAddr) == DKBIDENTIFIER &&
BU_REG_READ(DkbiPatternAddr+4) == DKBIDENTIFIER)
{
BU_REG_WRITE(DkbiPatternAddr, 0);
BU_REG_WRITE(DkbiPatternAddr+4, 0);
return 1;
}
return 0;
}
UINT_T PlatformInitFlash(pTIM pTIM_h)
{
UINT_T Retval = NoError;
UINT_T FlashNumber;
if(IsTim4DKBI(pTIM_h)) {
/* No valid flash ID in TIMH for DKBI */
return NoError;
}
FlashNumber = (pTIM_h->pConsTIM->FlashInfo.BootFlashSign) & 0xFF;
if(!FlashInitDone)
{
// Determine the Flash that will be used to store the downloaded images.
Retval = Configure_Flashes (FlashNumber, BOOT_FLASH);
if( Retval == NoError)
{
InitializeDualTimEarly(pTIM_h);
//turn on the flash management
InitializeFM(LEGACY_METHOD, BOOT_FLASH);
#if !UPDATER
InitializeDualTimLate(pTIM_h);
#endif
#ifdef BBM_LEGACY_EXT && !UPDATER
DetectScrubBitflipBlocks();
#endif
FlashInitDone = 1;
obm_printf("Flash Init Done\n\r");
}
}
return Retval;
}
//////////////////////////////////////////////////////////////////////
// This is the Mode Determining function for the Boot Loader.
//
// Inputs: A pointer to the Fuses and a pointer to the TIM.
// Outputs: Returns one of the following operationg modes:
// UPGRADESW, DOWNLOAD, SINGLE_TIM_BOOT, or DUAL_TIM_BOOT
//
// It mainly functions as following:
// 1) Sets running as identifier.
// 2) Check if the HW upgrade request button is pressed. If so, mode = UPGRADESW.
// 3) Else if there is a DKB ID in the TIM, then mode = DOWNLOAD.
// 4) Else if there is a DUAL TIM ID in the TIM, then mode = DUAL_TIM_BOOT.
// 5) Else by default, we have mode = SINGLE_TIM_BOOT.
//////////////////////////////////////////////////////////////////////
OPERATING_MODE_T DetermineOperatingMode(FUSE_SET *pFuses, pTIM pTIM_h)
{
OPERATING_MODE_T mode = SINGLE_TIM_BOOT;
UINT_T loops;
UINT gotkey = 0, Retval;
P_FOTA_Firmware pFOTA_T = NULL;
UINT_T ATDL_Cfg = 0;
PlatformInitFlash(pTIM_h);
// No user request for software update.
// Deterimine from TIM headers what to do.
// If DKB Identifier is found then we have download mode.
if (IsTim4DKBI(pTIM_h) || FindDkbiPatternInTIM(pTIM_h))
{
mode = DOWNLOAD;
time_count_enable = FALSE;
obm_printf("Empty Board or Force Download\n\r");
return mode;
}
ATDL_Cfg = GetAutoDownloadConfig(pTIM_h);
if (ATDL_Cfg) {
mode = UPGRADESW;
time_count_enable = TRUE;
time_count_in_sec = ATDL_Cfg;
obm_printf("ATDL download\n\r");
return mode;
}
if(PlatformCheckForceUSBEnumFlag())
{
mode = UPGRADESW;
time_count_enable = TRUE;
PlatformClearForceUSBEnumFlag();
obm_printf("Download due to force usb enum flag\n\r");
return mode;
}
if(serial_read_byte() == 0x0D) /* CR pressed */
{
mode = UPGRADESW;
time_count_enable = TRUE;
obm_printf("Uart force to download\n\r");
return mode;
}
pFOTA_T = OTAGetConfig(pTIM_h);
if(pFOTA_T && (pFOTA_T->dlflag[0] == DLFLG) && (pFOTA_T->dlflag[1] != DLDONE))
{
mode = UPGRADESW;
time_count_enable = FALSE;
obm_printf("Wait for unfinished download...\n\r");
return mode;
}
#if TRUSTED
if (pFOTA_T && (pFOTA_T->TrustBootStatus != 0) && (pFOTA_T->TrustBootStatus != -1))
{
Retval = pFOTA_T->TrustBootStatus;
pFOTA_T->TrustBootStatus = TB_NO_ERROR;
OTA_Save_Config(pTIM_h);
#if TB_ERROR_DOWNLOAD
obm_printf("Force Download Due to Trust Boot Error: %d\n\r", Retval);
mode = UPGRADESW;
time_count_enable = FALSE;
return mode;
#else
obm_printf("Dead Loop Due to Trust Boot Error: %d\n\r", Retval);
while(1);
#endif
}
#endif
if ( PlatformUSBPluggedIn() )
{
obm_printf("USB cable plugged in\n\r");
if(ASRFlag_InProductionMode(pTIM_h))
{
if(pOBM2OSL_h->booting_mode != OBMNODL && OBMNODL_RST_FLAG != OBMNODL)
{
mode = UPGRADESW;
time_count_enable = TRUE; // this is active enumeration by OBM if USB is connect, need to set timeout
obm_printf("[PROD]Enum USB port\n\r");
}else{
obm_printf("[PROD]Don't enum USB port\n\r");
}
OBMNODL_RST_FLAG= 0;
} else {
UINT_T DlFlag = BU_REG_READ(OBMDL_DDR_ADDRES);
if(DlFlag == OBMDL)
{
BU_REG_WRITE(OBMDL_DDR_ADDRES, 0x0);
mode = UPGRADESW;
time_count_enable = FALSE;
obm_printf("[Non-Prod]Wait for download(AT)...\n\r");
}else {
obm_printf("[Non-Prod]Don't enum USB port\n\r");
}
}
return mode;
}
#if UPDATE_USE_GPIO
for(loops = 0; loops < 10; loops++)
{
gotkey = PlatformCheckGPIOKey();
if (gotkey)
break;
}
switch (gotkey)
{
case KEYID_UPGRADESW_REQUEST:
mode = UPGRADESW;
time_count_enable = TRUE;
break;
case KEYID_PRODUCT_USB_MODE_REQUEST:
product_mode_select = PRODUCT_USB_MODE;
obm_printf("Production mode detected\n\r");
break;
default:
break;
}
#endif
return mode;
}
//////////////////////////////////////////////////////////////////////
// This is the Finalization Setup function for the Boot Loader.
//
// Inputs: A pointer to the fuses and a pointer to the TIM.
// Outputs: NONE.
//
// It mainly functions as following:
// 1) Shut down all the ports.
// 2) Shuts down the security API.
// 3) Finalize Flashes.
// 4) Disable interrupts.
// 5) Restore platform default configuration.
//////////////////////////////////////////////////////////////////////
void FinalizeSetup(pFUSE_SET pFuses, pTIM pTIM_h )
{
UINT_T retval = NoError;
#if TRUSTED
// Shut down the security API.
//if (pFuses->bits.SBE)
{
retval = SecurityShutdown();
if( retval != NoError)
{
FatalError(retval, "FS", NULL);
}
}
#endif
// Finalize the Flashes.
Finalize_Flashes(BOOT_FLASH); // this will flush the BBT information if necessary.
// Disable interrupts.
DisableIrqInterrupts();
}