/*---------------------------------------------------------------------------*
  Project:  TwlSDK - OS
  File:     os_application_jump.c

  Copyright 2007-2008 Nintendo. All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law. They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Date:: 2008-12-08#$
  $Rev: 9571 $
  $Author: okajima_manabu $
 *---------------------------------------------------------------------------*/

#include <twl/os.h>
#include <application_jump_private.h>
#include <twl/spi.h>
#include <twl/code32.h>
#include <twl/mcu.h>
#include <twl/hw/common/mmap_shared.h>
#include <twl/hw/common/mmap_parameter.h>

// Define data-------------------------------------------------------
#define OSi_LAUNCHER_PARAM_MAGIC_CODE           "TLNC"
#define OSi_LAUNCHER_PARAM_MAGIC_CODE_LEN       4
#define OSi_TITLE_ID_MEDIA_FLAG_MASK            ( 0x0001ULL << 34 )
#define OSi_TITLE_ID_DATA_ONLY_FLAG_MASK        ( 0x0001ULL << 35 )
#define OSi_TMP_APP_SIZE_MAX                    ( 16 * 1024 * 1024 )

#define OSi_TMP_APP_PATH_RAW                    "nand:/tmp/jump.app"
#define OSi_TMP_APP_PATH_RAW_LENGTH             18

typedef struct NandFirmResetParameter {
    u8      isHotStart :1;
    u8      isResetSW :1;
    u8      rsv :5;
    u8      isValid :1;
}NandFirmResetParameter;

// NAND firmware parameter address
#define OSi_GetNandFirmResetParam()         ( (NandFirmResetParameter *)HW_NAND_FIRM_HOTSTART_FLAG )

// Launcher parameter address
#define OSi_GetLauncherParam()              ( (LauncherParam *)HW_PARAM_LAUNCH_PARAM )


// Extern data-------------------------------------------------------

// Function's prototype----------------------------------------------
static void OSi_DoHardwareReset( void );

// Global variables--------------------------------------------------

// Static variables--------------------------------------------------
#include  <twl/ltdmain_begin.h>
static OSTitleId s_prevTitleId = 0;
#include  <twl/ltdmain_end.h>
// Const data--------------------------------------------------------

#ifdef SDK_ARM9

void OSi_InitPrevTitleId( void )
{
    OSDeliverArgInfo deliverArgInfo;
    OS_InitDeliverArgInfo( &deliverArgInfo, 0 );
    if ( OS_DELIVER_ARG_SUCCESS == OS_DecodeDeliverArg() )
    {
        s_prevTitleId = OS_GetTitleIdFromDeliverArg();
    }
    OS_SetDeliverArgStateInvalid();
}

// For safety purposes, locate this function in itcm
#include <twl/itcm_begin.h>
static void OSi_DoHardwareReset( void )
{
    // [TODO:] Lastly, change to HW reset via a microcomputer
    // Issue reset command
    PM_ForceToResetHardware();
    OS_Terminate();
}

#include <twl/itcm_end.h>

/* The following code is located in the TWL extended memory region */
#ifdef    SDK_TWL
#include  <twl/ltdmain_begin.h>
#endif
// Hardware reset by specifying LauncherParam
void OS_SetLauncherParamAndResetHardware( OSTitleId id, LauncherBootFlags *flag )
{
    // Copy the manufacturer and game codes
    u32 *maker_code_dest_addr = (u32 *)((u32)(OSi_GetLauncherParam()) + HW_PARAM_DELIVER_ARG_MAKERCODE_OFS);
    u32 *game_code_dest_addr = (u32 *)((u32)(OSi_GetLauncherParam()) + HW_PARAM_DELIVER_ARG_GAMECODE_OFS);
    u16 *maker_code_src_addr = (u16 *)(HW_TWL_ROM_HEADER_BUF + 0x10);
    u32 *game_code_src_addr = (u32 *)(HW_TWL_ROM_HEADER_BUF + 0xc);
    *maker_code_dest_addr = (u32)*maker_code_src_addr;
    *game_code_dest_addr = *game_code_src_addr;

    // Set the launcher parameter
    OSi_GetLauncherParam()->body.v1.prevTitleID = *(OSTitleId *)(HW_TWL_ROM_HEADER_BUF + 0x230);
    OSi_GetLauncherParam()->body.v1.bootTitleID = id;
    OSi_GetLauncherParam()->body.v1.flags = *flag;
    MI_CpuCopyFast( OSi_LAUNCHER_PARAM_MAGIC_CODE, (char *)&OSi_GetLauncherParam()->header.magicCode, OSi_LAUNCHER_PARAM_MAGIC_CODE_LEN);
    OSi_GetLauncherParam()->header.bodyLength = sizeof(LauncherParamBody);
    OSi_GetLauncherParam()->header.crc16 = SVC_GetCRC16( 65535, &OSi_GetLauncherParam()->body, OSi_GetLauncherParam()->header.bodyLength );
    OSi_GetLauncherParam()->header.version = 1;

    if( id == 0 || OS_IsDeliverArgEncoded() != TRUE || OS_GetTitleIdLastEncoded() != OSi_GetLauncherParam()->body.v1.prevTitleID )
    {
        // If the DeliverArg is not encoded with the current application, force initialization of DeliverArg and encode
        OSDeliverArgInfo deliverArgInfo;
        OS_InitDeliverArgInfo( &deliverArgInfo, 0 );
        (void)OS_EncodeDeliverArg();
    }

    DC_FlushRange((const void *)HW_PARAM_DELIVER_ARG, HW_PARAM_DELIVER_ARG_SIZE + HW_PARAM_LAUNCH_PARAM_SIZE );
    DC_WaitWriteBufferEmpty();

    OSi_DoHardwareReset();
}

// Function that returns from the application after the application jump to the original application
// If there is no original application, FALSE
BOOL OS_ReturnToPrevApplication( void )
{
    if( s_prevTitleId != 0)
    {
        return OS_DoApplicationJump( s_prevTitleId, OS_APP_JUMP_NORMAL ); //Never return
    }
    return FALSE;
}

static inline BOOL IsSameTitleID( OSTitleId titleID, OSTitleId listedTitleID )
{
    if ((char)titleID == 'A')
    {
        // Corresponding to language code A option
        return (listedTitleID >> 8) == (titleID >> 8);
    }
    else
    {
        return listedTitleID == titleID;
    }
}

// Get the ID that can actually be started for the given title ID
// If a startable ID exists, set that ID to the *bootableTitleID and return TRUE
// If a startable ID does not exist, return FALSE
static BOOL OS_GetBootableTitleID( OSTitleId titleID, OSTitleId *bootableTitleID )
{
    const OSTitleIDList *list = (const OSTitleIDList *)HW_OS_TITLE_ID_LIST;
    const int OS_TITLE_ID_LIST_MAX = sizeof(list->TitleID) / sizeof(*list->TitleID);
    const int num = MATH_MIN(list->num, OS_TITLE_ID_LIST_MAX);
    int i;
    for (i = 0; i < num; ++i)
    {
        if (IsSameTitleID(titleID, list->TitleID[i]) && (list->appJumpFlag[i / 8] & (u8)(0x1 << (i % 8))))
        {
            *bootableTitleID = list->TitleID[i];
            return TRUE;
        }
    }
    return FALSE;
}

// Whether an application has been installed that corresponds to that TitleID
BOOL OSi_CanApplicationJumpTo( OSTitleId titleID )
{
    return OS_GetBootableTitleID(titleID, &titleID);
}

// Wrapper for OS_SetLauncherParamAndResetHardware
BOOL OS_DoApplicationJump( OSTitleId id, OSAppJumpType jumpType )
{
    FSFile  file[1];
    LauncherBootFlags flag;
    u8 platform_code;
    u8 bit_field;
    
    switch( jumpType )
    {
        case OS_APP_JUMP_NORMAL:
            if( id & OSi_TITLE_ID_DATA_ONLY_FLAG_MASK )
            {
                // Set so that it is not possible to jump to DataOnly items
                return FALSE;
            }
            if( id != 0 )
            {
                if (!OS_GetBootableTitleID(id, &id))
                {
                    return FALSE;
                }
            }
            // Set the bootType and jump
            if( id & OSi_TITLE_ID_MEDIA_FLAG_MASK )
            {
                flag.bootType = LAUNCHER_BOOTTYPE_NAND;
            }else
            {
                flag.bootType = LAUNCHER_BOOTTYPE_ROM;
            }
            break;
        case OS_APP_JUMP_TMP:
            // When you yourself are a TMP application, TMP jump is not possible
            if( OS_IsTemporaryApplication() )
            {
                OS_TPrintf("OS_DoApplicationJump error : tmp app can't jump to tmp app!\n");
                return FALSE;
            }
            // Get the TitleID from the file of OS_TMP_APP_PATH and check the allowed TMP jump bits
            FS_InitFile( file );
            if( !FS_OpenFileEx(file, OS_TMP_APP_PATH, FS_FILEMODE_R) )
            {
                OS_TPrintf("OS_DoApplicationJump error : tmp app open error!\n");
                return FALSE;
            }
            // File size check
            if( FS_GetFileLength( file ) > OSi_TMP_APP_SIZE_MAX )
            {
                OS_TPrintf("OS_DoApplicationJump error : too large tmp app size!\n");
                return FALSE;
            }
            
            if( !FS_SeekFile( file, 0x12, FS_SEEK_SET ) ||
                ( sizeof(platform_code) != FS_ReadFile( file, &platform_code, sizeof(platform_code) ) ) ||
                !FS_SeekFile( file, 0x1d, FS_SEEK_SET ) ||
                ( sizeof(bit_field) != FS_ReadFile( file, &bit_field, sizeof(bit_field) ) )
            )
            {
                OS_TPrintf("OS_DoApplicationJump error : tmp app read error!\n");
                (void)FS_CloseFile(file);
                return FALSE;
            }
            
            if( !( bit_field & 0x2 ) )
            {
                OS_TPrintf("OS_DoApplicationJump error : tmp jump bit is not enabled!\n");
                (void)FS_CloseFile(file);
                return FALSE;
            }
            flag.bootType = LAUNCHER_BOOTTYPE_TEMP;
            // The TWL application gets the title ID from the header and enters it. For NTR applications, for the time being enter TitleIDs other than 0
            if( platform_code & 0x2 )
            {
                if( !FS_SeekFile( file, 0x0230, FS_SEEK_SET ) ||
                    ( sizeof(id) != FS_ReadFile( file, &id, sizeof(id) ) )
                )
                {
                    OS_TPrintf("OS_DoApplicationJump error : tmp app read error!\n");
                    (void)FS_CloseFile(file);
                    return FALSE;
                }
            }else
            {
                id = 0x1;
            }
            (void)FS_CloseFile(file);
            break;
        default:
            return FALSE;
    }

    if ( PMi_TryLockForReset() == FALSE )
    {
        return FALSE;
    }
    
    // Shared configurations
    flag.isValid = TRUE;
    flag.isLogoSkip = TRUE;
    flag.isInitialShortcutSkip = FALSE;
    flag.isAppLoadCompleted = FALSE;
    flag.isAppRelocate = FALSE;
    flag.rsv = 0;
    
    OS_SetLauncherParamAndResetHardware( id, &flag ); // never return.
    return TRUE;
}

/* The code above is located in the TWL extended memory region */
#ifdef    SDK_TWL
#include  <twl/ltdmain_end.h>
#endif

/*---------------------------------------------------------------------------*
  Name:         OS_JumpToSystemMenu

  Description:  Run a hardware reset and jump to the system menu
  
  Arguments:    None.
 
  Returns:      FALSE ... Application jump failed for some reason   
                * If the process was successful, a reset process occurred during this function so TRUE is not returned
                   
 *---------------------------------------------------------------------------*/
BOOL OS_JumpToSystemMenu( void )
{
    BOOL result = FALSE;
#ifdef SDK_TWL
    if( OS_IsRunOnTwl() )
    {
        // ---- Application Jump with id==0
        //      means to jump to system menu.
        result = OS_DoApplicationJump( 0, OS_APP_JUMP_NORMAL );
    }
    else
#endif
    {
        OS_Warning("This Hardware don't support this funciton");
    }
    return result;
}

/*---------------------------------------------------------------------------*
  Name:         OS_RebootSystem

  Description:  Runs a hardware reset and restarts itself.
  
  Arguments:    None.
 
  Returns:      FALSE      Run on NITRO, or failure in restarting
                * If the process was successful, a reset occurred during this function so TRUE is not returned
                   
 *---------------------------------------------------------------------------*/
BOOL OS_RebootSystem( void )
{
#ifdef SDK_TWL
    if( OS_IsRunOnTwl() )
    {
        if( OS_IsTemporaryApplication() )
        {
            OS_TPrintf("OS_RebootSystem error : tmp app can't execute this function\n");
            return FALSE;
        }
        // Application jump to self
        return OS_DoApplicationJump( OS_GetTitleId(), OS_APP_JUMP_NORMAL );
    }
    else
#endif
    {
        OS_Warning("This Hardware don't support this funciton");
        return FALSE;
    }
}

/*---------------------------------------------------------------------------*
  Name:         OS_IsRebooted

  Description:  Checks whether a restart was applied using OS_RebootSystem
  
  Arguments:    None.
 
  Returns:      TRUE   Restart was applied more than one time
                FALSE  First startup
 *---------------------------------------------------------------------------*/
BOOL OS_IsRebooted( void )
{
    BOOL result = FALSE;
#ifdef SDK_TWL
    if( OS_IsRunOnTwl() )
    {
        if( OS_GetTitleId() == s_prevTitleId )
        {
            result = TRUE;
        }
    }
#endif
    return result;
}

/*---------------------------------------------------------------------------*
  Name:         OS_IsTemporaryApplication

  Description:  Checks whether self is a TMP application.
  
  Arguments:    None.
 
  Returns:      TRUE    TMP application
                FALSE  Other than TMP applications
 *---------------------------------------------------------------------------*/
BOOL OS_IsTemporaryApplication(void)
{
#ifdef SDK_TWL
    static BOOL isChecked = FALSE, isTmpApp = FALSE;
    if( OS_IsRunOnTwl() )
    {
        if( isChecked == FALSE ) /* If already checked, flow previous results */
        {
            if( 0 == STD_CompareNString( OS_GetBootSRLPath(), OSi_TMP_APP_PATH_RAW, OSi_TMP_APP_PATH_RAW_LENGTH ) )
            {
                isTmpApp = TRUE;
            }
            else
            {
                isTmpApp = FALSE;
            }
            isChecked = TRUE;
        }
        return isTmpApp;
    }
#endif
    return FALSE;
}

#else // SDK_ARM9
    //----------------------------------------------------------------
    // For ARM7

static BOOL OSi_IsValidLauncherParam( void )
{
    return ( STD_StrNCmp( (const char *)&OSi_GetLauncherParam()->header.magicCode,
                             OSi_LAUNCHER_PARAM_MAGIC_CODE,
                             OSi_LAUNCHER_PARAM_MAGIC_CODE_LEN ) == 0 ) &&
    ( OSi_GetLauncherParam()->header.bodyLength > 0 ) &&
    ( OSi_GetLauncherParam()->header.crc16 == SVC_GetCRC16( 65535, &OSi_GetLauncherParam()->body, OSi_GetLauncherParam()->header.bodyLength ) );
}

static BOOL OSi_IsEnableJTAG( void )
{
    // If SCFG register is invalid, the SCFG register value becomes "0". Check the value retracted to WRAM.
    u8 value = (u8)(( reg_SCFG_EXT & REG_SCFG_EXT_CFG_MASK ) ?
                    ( reg_SCFG_JTAG & REG_SCFG_JTAG_CPUJE_MASK ) :
                    ( *(u8 *)HWi_WSYS09_ADDR & HWi_WSYS09_JTAG_CPUJE_MASK ));
    return value ? TRUE : FALSE;
}

// Launcher parameter lead and determine hot/cold boot
// Return TRUE if launcher parameter is valid; FALSE if invalid
// If the launcher parameter in LauncherParam is valid, that launcher parameter is stored
// TRUE if hot boot in isHotstart; False is stored if a cold boot.
BOOL OS_ReadLauncherParameter( LauncherParam *buf, BOOL *isHotstart )
{
    if( !OSi_GetNandFirmResetParam()->isValid ) {
        *(u8 *)OSi_GetNandFirmResetParam() = (u8)MCU_GetFreeRegister( OS_MCU_RESET_VALUE_OFS );
        OSi_GetNandFirmResetParam()->isValid = 1;
    }
    
    // Hot/cold boot determination
    // If "HOTBT flag of a microcomputer free register = 0," cold boot
    if( !OSi_GetNandFirmResetParam()->isHotStart ) {
        *isHotstart = FALSE;
    }else {
        *isHotstart = TRUE;
        // Determine launcher parameter validity
        if(  OSi_IsValidLauncherParam() &&
            !OSi_GetNandFirmResetParam()->isResetSW
        ) {
            // If launcher parameter is valid, copy to buf
            MI_CpuCopy32 ( OSi_GetLauncherParam(), buf, sizeof(LauncherParam) );
            return TRUE;
        }else {
            // Else, clear the transfer destination buffer
            MI_CpuClear32 ( buf, sizeof(LauncherParam) );
        }
    }
    return FALSE;
}

#endif // SDK_ARM9
