Found a way to start a second app from Flash with minimal effort. The main function is jump_to_application with two parameters, the address of the execution and stack pointer. Works with both regular applications and encrypted ones:
#include <stdint.h>
#include "fsl_common.h"
#include "fsl_debug_console.h"
//#define BL_APP_VECTOR_TABLE_ADDRESS (0x60002000u)
#define BL_APP_VECTOR_TABLE_ADDRESS (0x60032000u)
#define APP_VECTOR_TABLE ((uint32_t *)BL_APP_VECTOR_TABLE_ADDRESS)
enum _vector_table_entries
{
kInitialSP = 0, //!< Initial stack pointer.
kInitialPC = 1, //!< Reset vector.
};
typedef enum _shutdown_types
{
kShutdownType_Shutdown = 0,
kShutdownType_Cleanup = 1,
kShutdownType_Reset = 2,
} shutdown_type_t;
enum _vector_table_address
{
//! @brief Address of the default vector table, which is always 0.
kDefaultVectorTableAddress = 0
};
/** \brief Clear Enabled IRQs
The function clears all device IRQs
*/
__STATIC_INLINE void NVIC_ClearEnabledIRQs(void)
{
NVIC->ICER[0] = 0xFFFFFFFF;
NVIC->ICER[1] = 0xFFFFFFFF;
NVIC->ICER[2] = 0xFFFFFFFF;
NVIC->ICER[3] = 0xFFFFFFFF;
NVIC->ICER[4] = 0xFFFFFFFF;
NVIC->ICER[5] = 0xFFFFFFFF;
NVIC->ICER[6] = 0xFFFFFFFF;
NVIC->ICER[7] = 0xFFFFFFFF;
}
/** \brief Clear All Pending Interrupts
The function clears the pending bits of all external interrupts.
*/
__STATIC_INLINE void NVIC_ClearAllPendingIRQs(void)
{
NVIC->ICPR[0] = 0xFFFFFFFF;
NVIC->ICPR[1] = 0xFFFFFFFF;
NVIC->ICPR[2] = 0xFFFFFFFF;
NVIC->ICPR[3] = 0xFFFFFFFF;
NVIC->ICPR[4] = 0xFFFFFFFF;
NVIC->ICPR[5] = 0xFFFFFFFF;
NVIC->ICPR[6] = 0xFFFFFFFF;
NVIC->ICPR[7] = 0xFFFFFFFF;
}
void init_interrupts(void)
{
// Clear any IRQs that may be enabled, we only want the IRQs we enable to be active
NVIC_ClearEnabledIRQs();
// Clear any pending IRQs that may have been set
NVIC_ClearAllPendingIRQs();
}
void shutdown_cleanup(shutdown_type_t shutdown)
{
// If we are permanently exiting the bootloader, there are a few extra things to do.
if (shutdown == kShutdownType_Shutdown)
{
#if 0
// Shutdown microseconds driver.
microseconds_shutdown();
#endif
// Disable force ROM.
#if defined(RCM_FM_FORCEROM_MASK)
RCM->FM = ((~RCM_FM_FORCEROM_MASK) & RCM->FM) | RCM_FM_FORCEROM(0);
#elif defined(SMC_FM_FORCECFG_MASK)
#if defined(SMC0)
SMC0->FM = ((~SMC_FM_FORCECFG_MASK) & SMC0->FM) | SMC_FM_FORCECFG(0);
#else
SMC->FM = ((~SMC_FM_FORCECFG_MASK) & SMC->FM) | SMC_FM_FORCECFG(0);
#endif
#endif // defined(RCM_FM_FORCEROM_MASK)
// Clear status register (bits are w1c).
#if defined(RCM_MR_BOOTROM_MASK)
RCM->MR = ((~RCM_MR_BOOTROM_MASK) & RCM->MR) | RCM_MR_BOOTROM(3);
#elif defined(SMC_MR_BOOTCFG_MASK)
#if defined(SMC0)
SMC0->MR = ((~SMC_MR_BOOTCFG_MASK) & SMC0->MR) | SMC_MR_BOOTCFG(3);
#else
SMC->MR = ((~SMC_MR_BOOTCFG_MASK) & SMC->MR) | SMC_MR_BOOTCFG(3);
#endif
#endif // defined(RCM_MR_BOOTROM_MASK)
init_interrupts();
// Set the VTOR to default.
SCB->VTOR = kDefaultVectorTableAddress;
#if 0
// Restore clock to default before leaving bootloader.
configure_clocks(kClockOption_ExitBootloader);
// De-initialize hardware such as disabling port clock gate
deinit_hardware();
#endif
// Restore global interrupt.
__enable_irq();
#if BL_FEATURE_BYPASS_WATCHDOG
// De-initialize watchdog
bootloader_watchdog_deinit();
#endif // BL_FEATURE_BYPASS_WATCHDOG
}
// Memory barriers for good measure.
__ISB();
__DSB();
}
static void jump_to_application(uint32_t applicationAddress, uint32_t stackPointer)
{
shutdown_cleanup(kShutdownType_Shutdown);
// Create the function call to the user application.
// Static variables are needed since changed the stack pointer out from under the compiler
// we need to ensure the values we are using are not stored on the previous stack
static uint32_t s_stackPointer = 0;
s_stackPointer = stackPointer;
static void (*farewellBootloader)(void) = 0;
farewellBootloader = (void (*)(void))applicationAddress;
// Set the VTOR to the application vector table address.
SCB->VTOR = (uint32_t)APP_VECTOR_TABLE;
// Set stack pointers to the application stack pointer.
__set_MSP(s_stackPointer);
__set_PSP(s_stackPointer);
// Jump to the application.
farewellBootloader();
// Dummy fcuntion call, should never go to this fcuntion call
shutdown_cleanup(kShutdownType_Shutdown);
}
void go_to_app(void)
{
uint32_t stackPointer = 0;
uint32_t applicationAddress = 0;
stackPointer = APP_VECTOR_TABLE[kInitialSP];
applicationAddress = APP_VECTOR_TABLE[kInitialPC];
PRINTF("Start application at addr =0x%X stackPointer=0x%X \r\n",applicationAddress,stackPointer);
jump_to_application(applicationAddress, stackPointer);
}