Start bootloader from firmware - c

I wish to be able to start the bootloader directly from the code without having to have a pin high and reset the microcontroller to access it. The idea below is that the bootloader binary is stored in a char array. When Start_BootLoader() is called, the bootloader is copied into SRAM and executed.
However, the code copies into RAM but when it attempts to execute the code at the location I have copied it to, it does nothing.
The micro. is an Energy Micro EFM32380f1024. The code below that I am using is based on Energy Micro's application note AN0042.
void Start_Bootloader(void)
{
/* Diable interrupts */
INT_Disable();
__set_MSP( ( 0x20000000 + sizeof( bootloader ) + 0x400 ) & 0xFFFFFFF0 );
/* Load the entire bootloader into SRAM. */
memcpy( (void*)0x20000000, bootloader, sizeof( bootloader ) );
/* Start executing the bootloader. */
BOOT_jump( *(uint32_t*)0x20000000, *(uint32_t*)0x20000004 );
}

The code ships with ROM and RAM linker settings - but only the RAM version will likely work in your case. Try to read the second word (32 Bit little endian) from your binary - it should point to an odd address in the 0x20000000 range, as it is the new PC value.

Related

NULL pointer protection with ARM Cortex-M MPU

The MPU in ARM Cortex-M (M0+/M3/M4/M7/etc.) is often advertised as allowing to set up protection against dereferencing the NULL pointer. But how to do this in practice? (Some online discussions, like in the Zephyr Project, indicate that the issue is not quite trivial.)
I'm looking for the simplest possible MPU code running in "Privileged mode" on bare-metal ARM Cortex-M. Please note that "protection against dereferencing the NULL pointer" means to me protection both against reads and writes. Also, it is not just about the address 0x0, but small offsets from it as well. For example, accessing a struct member via a NULL pointer should also cause MPU exception:
struct foo {
. . .
uint8_t x;
};
. . .
uint8_t x = (*(struct foo volatile *)NULL)->x; // should fail!
After some experimentation, I've come up with the MPU setting that seems to work for most ARM Cortex-M MCUs. Here is the code (using the CMSIS):
/* Configure the MPU to prevent NULL-pointer dereferencing ... */
MPU->RBAR = 0x0U /* base address (NULL) */
| MPU_RBAR_VALID_Msk /* valid region */
| (MPU_RBAR_REGION_Msk & 7U); /* region #7 */
MPU->RASR = (7U << MPU_RASR_SIZE_Pos) /* 2^(7+1) region, see NOTE0 */
| (0x0U << MPU_RASR_AP_Pos) /* no-access region */
| MPU_RASR_ENABLE_Msk; /* region enable */
MPU->CTRL = MPU_CTRL_PRIVDEFENA_Msk /* enable background region */
| MPU_CTRL_ENABLE_Msk; /* enable the MPU */
__ISB();
__DSB();
This code sets up a no-access MPU region #7 around the address 0x0 (any other MPU region will do as well). This works even for the MCUs, where the Vector Table also resides at address 0x0. Apparently, the MPU does not check access to the region by instructions other than LDR/STR, such as reading the vector address during Cortex-M exception entry.
However, in case the Vector Table resides at 0, the size of the no-access region must not contain any data that the CPU would legitimately read with the LDR instruction. This means that the size of the no-access region should be about the size of the Vector Table. In the code above, the size is set to 2^(7+1)==256 bytes, which should be fine even for relatively small vector tables.
The code above works also for MCUs that automatically relocate the Vector Table, such as STM32. For these MCUs, the size of the no-access region can be increased all the way to the relocated Vector Table, like 0x0800'0000 in the case of STM32. (You could set the size to 2^(26+1)==0x0800'0000).
Protection against NULL-pointer dereferencing is an important tool for improving the system's robustness and even for preventing malicious attacks. I hope that this answer will help fellow embedded developers.

In-Application Program jump from user bootloader to user application and viceversa on STM32F446RE board

I've got a question.
I'm developing an IAP (In-Application Programming) tool for my STM32F446RE board and I'm stuck.
I've developed all the necessary utilities in order to let to the microcontroller to receive a binary (.bin) compiled file from a GUI, write it on a specific flash memory sector and execute it.
My problem comes when, from the uploaded code, I want to jump again to the bootloader that is stored on the flash memory sector 0, I see that the code does not jump to the bootloader but, instead, it continues the execution of the user application code. I've debugged the code and I seen that all the addresses (the msp and the reset handler) of the bootloader code are correctly set and they are different compared to the ones of the uploaded code.
The flow that i want to achieve is the following:
1 --> Execute the bootloader code stored on sector 0 (starting at address 0x0800 0000, when an interrupt from the User button is received) and write the newly received code into the sector 2 (starting at address 0x0800 8000)
2 --> set the msp address (#0x0800 8000) and the reset handler address (0x0800 8004)
3 --> jump to the reset handler address of the new code (#0x0800 8004)
4 --> execute the new uploaded code.
5 --> during the user code execution, if an interrupt is received (from user push button) then set the bootloader msp address, the reset handler and jump to the bootloader
6 --> repeat again from step one.
This is the code used to jump from the bootloader to the user application:
IAP_loadProgram(&data);
//pointer to the user application reset handler address
void (*user_resetHandler)(void);
//set the user application MSP address (user application starts on the flash SECTOR2
uint32_t msp_addr = *(volatile uint32_t *)APPLICATION_ADDRESS;
__set_MSP(msp_addr);
//Set now the addres of the reset handler
uint32_t resetAddr = *(volatile uint32_t *)(APPLICATION_ADDRESS + 4);
user_resetHandler = (void *)resetAddr;
//When there, the bootloader sector will be leaved and the user code execution starts
user_resetHandler();
Finally, the code used to jump from the user application code to the bootloader is:
if(toBootloader){
toBootloader = 0;
//pointer to the user application reset handler address
void (*bootLoader_resetHandler)(void);
//set the user application MSP address (user application starts on the flash SECTOR2
uint32_t msp_addr = *(volatile uint32_t *)BOOTLOADER_ADDRESS;
__set_MSP(msp_addr);
//Set now the address of the reset handler
uint32_t bootLoaderResetAddr = *(volatile uint32_t *)(BOOTLOADER_ADDRESS + 4);
bootLoader_resetHandler = (void *)bootLoaderResetAddr;
//When there, the user code sector will be leaved and the bootloader code execution starts
bootLoader_resetHandler();
}
Where APPLICATION_ADDRESS is 0x0800 8000 and BOOTLOADER_ADDRESS is 0x0800 0000.
The content of the first two addresses of the bootloader code is:
0x08000000: 20020000
0x08000004: 080044DD
meanwhile the content of the first two addresses of the application code is:
0x08008000: 20020000
0x08008004: 0800A1F1
Last modify that i've done is on the user application linker (.ld) file, where i set the flash start to the address 0x0800 8000 (instead of the address 0x0800 0000).
All the interrupts are correctly working and, after that the code has been uploaded, if I do a hardware reset, the result is the same, the code execution starts from the user application code, not from the bootloader.
Any tips?
Your problem description is unclear but the procedure of invoking the app is far not sufficient. You need to make sure that the environment for the application is same as after the uC reset. You need to change the vector table address as well.
I wrote tens of bootloaders but I do not understrand your problem
Here you have an example how it should be done (app call from bootloader)
void startAPP(void)
{
static uint32_t *pAppPosition;
static voidFunc *appResetHandler;
static uint32_t newSP;
pAppPosition = (uint32_t *)(bankStartAddress[0] + (uint32_t)&_BOOTFlashSize);
appResetHandler = (voidFunc *)pAppPosition[1];
newSP = pAppPosition[0];
SPI_DeInit();
FLASH_DeInit();
I2C_DeInit();
IRQ_DeInit();
GPIO_DeInit();
__disable_irq();
__set_MSP(newSP);
__enable_irq();
SCB -> ICSR = 0x00000000; // reset value;
SCB -> SCR = 0;
SCB -> CCR = 0x00000200; // reset value
SCB -> SHP[0] = 0;
SCB -> SHCSR = 0;
SCB -> CFSR = (SCB_CFSR_DIVBYZERO_Msk | SCB_CFSR_UNALIGNED_Msk | SCB_CFSR_UNDEFINSTR_Msk | SCB_CFSR_NOCP_Msk | SCB_CFSR_INVPC_Msk | SCB_CFSR_INVSTATE_Msk);
SCB -> HFSR = (SCB_HFSR_DEBUGEVT_Msk | SCB_HFSR_FORCED_Msk | SCB_HFSR_VECTTBL_Msk);
SCB -> VTOR = bankStartAddress[0] + (uint32_t)&_BOOTFlashSize; // new vector table pos. I od not clear 8 LSB because APP start position is aligned to FLASH Sectors which are at least 2k aligned
// SysTick
SysTick -> CTRL = 0;
SysTick -> LOAD = 0;
SysTick -> VAL = 0;
appResetHandler();
__builtin_unreachable();
}
The simplest and safest method of running the bootloader from the application is simply to issue a soft reset using the CMSIS NVIC_SystemReset() function.
if( toBootloader )
{
NVIC_SystemReset() ;
}
Jumping to the bootloader by a direct call is unnecessary and ill-advised. While it can be done, just as you can jump from the bootloader to the application, you need to at least disable interrupts/exceptions and switch the vector table from that of the application to that of the bootloader. Neither your application code nor bootloader code appear to be doing that. See ARM: How to Write a Bootloader for example.
Issuing a reset has the advantage of setting the processor and all on-chip peripherals and I/O into their known reset state so you do not need to worry about de-initialising the NVIC, or any peripherals that might generate an interrupt while you are switching vector tables.
If you need to communicate information to the bootloader from the application the state of the on-chip SRAM will survive the reset process, so you can reserve space that the run-time start-up will not initialise to pass parameters to the bootloader if you need to.

SysTick interrupt causes execution to jump to 0x1fffxxxx on STM32F030

I'm trying to use SysTick_Handler in SW4STM32 for Linux, but whenever the SysTick interrupt is triggered, execution jumps to somewhere in system memory. By my understanding, it should jump into the void SysTick_Handler(void) that I declared, or failing that, into Default_Handler declared in startup_stm32.s where the interrupt vector table is defined. I set a break point in my SysTick_Handler, but it is never reached. In the code below, it gets through init_systick() and stays in the endless for loop if I don't include SysTick_CTRL_TICKINT_Msk, as expected, but when I do include it, the debugger tells me it ends up somewhere around address 0x1fffda7c.
main.c:
#include "stm32f0xx.h"
volatile uint32_t ticks = 0;
void SysTick_Handler(void) {
ticks++;
}
void init_systick(void) {
SysTick->LOAD = 43999;
SCB->SHP[1] |= 0x40000000L;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
}
int main(void)
{
init_systick();
for(;;);
}
I verified from the .map file that the linker is using the declared SysTick_Handler instead of Default_Handler.
I also tried the following variation to use the standard peripheral library for setup, along with other interrupt priority values, with the same results:
#include "stm32f0xx.h"
volatile uint32_t ticks = 0;
void SysTick_Handler(void) {
ticks++;
}
void init_systick(void) {
SysTick_Config(44000);
NVIC_EnableIRQ(SysTick_IRQn);
NVIC_SetPriority(SysTick_IRQn, 0);
}
int main(void)
{
init_systick();
for(;;);
}
This shouldn't be relevant, but since the target doesn't have a timing crystal, I also modified void SetSysClock(void) in system_stm32f0xx.c to use the HSI clock and PLL, which appears to be working correctly:
static void SetSysClock(void)
{
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_HSI;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) ;
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
RCC->CR &= ~RCC_CR_PLLON;
while (RCC->CR & RCC_CR_PLLRDY) ;
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_PLLMUL & ~RCC_CFGR_PLLSRC) | RCC_CFGR_PLLMUL11; // PLL takes 8 MHz HSI / 2 as input
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY)) ;
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) ;
}
-- EDIT: More info requested in the comments --
It's an M0 core, so it doesn't have vector table relocation. From the reference manual section 2.5 (page 44):
Unlike Cortex ® M3 and M4, the M0 CPU does not support the vector table relocation.
Address 0x00000000 should be mapped either to FLASH memory at 0x08000000, system memory at 0x1fffd800, or to SRAM at 0x20000000. The memory at address 0x00000000 matches system memory at 0x1fffd800, even though SYSCFG_CFGR1 MEM_MODE is set to 00, which should map FLASH memory there. Main FLASH memory at address 0x08000000 contains the correct vector table, but address 0x00000000 is populated with address 0x1fffd99d for the SysTick vector (and all other non-NULL vectors except the reset vector, which is 0x1fffdc41); the vectors shown by the debugger at address 0x00000000 are consistent with the observed behavior. All of this information was collected while paused at a breakpoint at address 0x08000298 (a correct position in FLASH memory where the correct code has been loaded), before executing the interrupt.
The VTOR is referenced in the arm-v6 reference manual. It is a good idea to check this.
https://static.docs.arm.com/ddi0419/d/DDI0419D_armv6m_arm.pdf
0xE000ED08 VTOR RW 0x00000000a Vector Table Offset Register, VTOR on page B3-231
That reference manual is for the arm-v6M architecture which is the architecture for cortex-m0 processors. This is the bible. Most of the generic cortex-m0 features of the stm line will not be mentioned in stm refrenece manuals, just in the arm arm.
I'll reiterate, check the VTOR.
And make sure you are building for the right line of STM32F030!
The STM32F030x4 and STM32F030x6 micros have a different memory map than the STM32F030x8.
This sounds like there might be a problem with the linker file.
Can you verify that your linker file has something that looks like the following? (if it is a default file it will probably be far more complex).
. = 0;
.text 0 :
{
*(.vector);
crt0*(.text*);
main*(.text*);
*(.text*);
} > flash
Basically what this is saying is that the 'text' (code) of the program starts at address 0x0, and the first thing to put there is the vector table, followed by startup code, main, and then other code.
You will also then want to check that you have some file specifying this vector table's contents that also agrees it should be at address 0x0. This example is from an ATSAMD21E18A.
.section .vector, "a", %progbits
.equ stack_base, 0x20004000
.word stack_base
.word reset_handler
.word nmi_handler
.word hardfault_handler
.word 0
// ...
.word 0
.word systick_handler
// ...
The important thing is that it is marked as the section vector which the linker file will try to put at 0x0. For a processor with VTOR (like the M0+), this table might be specified in C without any special markings, and its location won't matter, but since you don't have VTOR, you need to make sure the linker knows to put this section right at 0x0.
jonnconn is correct. VTOR is the issue. I just wanted to add to his post (but cannot because my reputation isn't high enough) that I ran into this issue recently with a new CubeMx project.
VTOR is set to 0 on reset. VTOR is initialized in low level init before main. SystemInit in system_stm32l4xx.c (or similar for your part) seems to only initialize VTOR if USER_VECT_TAB_ADDRESS is defined. But CubeMx had this commented out. I had to uncomment it (it is in the same file), and leave VECT_TAB_OFFSET as 0. Afterwards, VTOR config was correctly set to FLASH_BASE (assuming VECT_TAB_SRAM was left undefined).
According the datasheet, that region is the system memory (for built-in bootloader). You may try two things:
Double check BOOTx pins to make sure the MCU loads FLASH instead of the system memory.
Make sure you assigned SCB->VTOR to the correct address of your own interrupt vector table.

Bootloader for Cortex M4 - Jump to loaded Application

I am using a Atmel SAM4E-16e on Atmel SAM4E-EK Board. I have written a bootloader for this configuration.
The bootloader receives the .bin-File via UART and writes it into Flash. This works without problems, i made a hex-dump and it was exactly what i expected:
Bootloader at 0x400000 (Flash Start Address of AT SAM4E)
My Application at 0x420000
0x800000 is Flash End Address
This is the C-Code:
int main(void){
// Init and downloading the .bin to Flash
binary_exc((void*) 0x420000);
}
int binary_exec(void * vStart){
int i;
// -- Check parameters
// Should be at least 32 words aligned
if ((uint32_t)vStart & 0x7F)
return 1;
Disable_global_interrupt();
// Disable IRQs
for (i = 0; i < 8; i ++) NVIC->ICER[i] = 0xFFFFFFFF;
// Clear pending IRQs
for (i = 0; i < 8; i ++) NVIC->ICPR[i] = 0xFFFFFFFF;
// -- Modify vector table location
// Barriars
__DSB();
__ISB();
// Change the vector table
SCB->VTOR = ((uint32_t)vStart & SCB_VTOR_TBLOFF_Msk);
// Barriars
__DSB();
__ISB();
Enable_global_interrupt();
// -- Load Stack & PC
_binExec(vStart);
return 0;
}
void _binExec (void * l_code_addr){
__asm__ ("mov r1, r0 \n"
"ldr r0, [r1, #4] \n" //I also tryed #5 but that doesn't work, too
"ldr sp, [r1] \n"
"blx r0"
);
}
But when i try to jump to my application, the Application does not start.
The code for jumping to the program is out of an example of Atmel for the SAM8X (Cortex M3). The debugger says sometimes that it the PC jumps to another Address (0x004003E2) instead, but does not go on.
I found the old topic Bootloader for Cortex M3 where the solution was to just add one but this doesn't work for me, even if i used their code. Then the debugger does not responds any more.
I am using Atmel Studio 7 with GCC. The processor runs in Thumb-Mode.
I hope you can help me to solve this problem or give me some tipps what is going wrong here.
This code assumes that program loaded at address 0x420000 starts with a vector table:
SP at offset 0 (0x420000)
Reset address at offset 4 (0x420004).
For this, the code seems perfectly correct.
But are you sure that this vector table is correct ? Is bit 0 of data at 0x420004 set as this is Thumb code? When you compile this code, is it aware that it will run from this address (For any absolute address it might use). Do you have the possibility to use a debugger to understand when the first fault occurs?
I think you should provide the disass of the first instructions of the program you try to load at this address.
I have solved the problem now.
I still use the code I posted in my question. The problem was that the .bin-file i write on my processor's flash at 0x420000 was compiled in a way that it thought it is at flash start address (0x400000).
When it has loaded the reset vector's address it was at 0x400xyz instead of 0x420xyz so the application jumped to the wrong address.
The solution was to Change the Flash start address to 0x420000 in the project I want to upload via bootloader.

LPC1768 load application to new memory offset

Question: - how to locate application to non 0x0000.0000 address?
Processor: NXP LPC1768
Dev system: Keil ARM 4.73
Steps used:
1) scatter file below used to set load region and execution region to 0x0000.2000
2) copied vector table to 0x2000
3) udpated vtor register to 0x2000
Problem: Application does not run.
Scatter file used:
LR_IROM1 0x00002000 0x00000D000
{ ; load region size_region
ER_IROM1 0x00002000 0x0000D000
{ ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x10000000 0x00008000 { ; RW data
.ANY (+RW +ZI)
}
}
This follows instructions specified in NXP app note AN10744, something else I’m missing?
Vector Table Offset Register (VTOR) points to 0x00000000 at reset.
Thus, stack pointer must be at 0x00000000, and program start address (program counter) at 0x00000004.
If you change the location of the vector table in linker settings, you need to update VTOR to point to this new location. This can only happen at runtime.
This means that you need to have a small bootloader program which does the remapping, which means that first sector must be reserved for that purpose.
Bootloader needs to:
Make sure that interrupts are disabled, so you don't accidentally use VTOR.
Update VTOR register to address 0x2000.
Get stack pointer address from 0x2000 and update stack pointer register.
Get program start address from 0x2004 and update the program counter.
You might want to check out CMSIS library, it has functions like NVIC_SetVTOR and __set_MSP which make setting these registers a little easier.
To set the program counter, you can cast the address to function pointer and then call the function:
uint32_t * vtor = (uint32_t *)0x2000;
uint32_t startAddr = vtor[1];
( (void(*)(void))startAddr )(); // Cast and call

Resources