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
Related
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.
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.
I know dereferencing a null pointer is undefined - but I would like to know what happens on a specific target - an MSP430.
I don't have a board to load this on in front of me to test this out right now.
What would happen if I did this (or similar)?
int * foo = NULL;
(*foo)++; //Crash?
Location 0x0 is in the SFR range and is reserved.
Would it generate a PUC/POR? Or would it silently "work"?
The assembly generated is
;int * foo = NULL;
clr.w R15
;(*foo)++;
inc.w R15
So location 0x0 is literally being incremented by 1.
When I run this in the simulator I see the value at address 0x0 go from 0 to 1. I get no warnings in the debug log and the program exits normally.
I am using the IAR EW430 compiler/assembler/simulator.
Not only writing and reading the address 0x0 will not cause a crash or a reboot, it actually a completely legal operation that is often used by MSP430 applications.
The initial portion or MSP430 memory map is reserved for I/O ports and control registers: http://en.wikipedia.org/wiki/TI_MSP430#MSP430_address_space
In particular, the control registers at 0x0 and subsequent addresses are:
#define IE1_ 0x0000 /* Interrupt Enable 1 */
#define IE2_ 0x0001 /* Interrupt Enable 2 */
#define IFG1_ 0x0002 /* Interrupt Flag 1 */
#define IFG2_ 0x0003 /* Interrupt Flag 2 */
So for example writing zero to that memory address by dereferencing a uint8_t * or uint16_t * pointer is going to disable interrupts. Writing zero by dereferencing an uint32_t * it is also going to clear the flags. Incrementing the value of these registers does not make a lot of sense, but should be completely legal.
At least this is the case on msp430 Series 1, Series 2 and Series 4. By checking the header files I was not able to find anything mapped to 0x0 on Series 5 (the interrupt control registers are mapped to region starting from 0x0100).
So if you want to catch places in code where the NULL pointer is dereferenced, you're completely on your own.
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.
I've written a bootloader for my SAM4S that sits in sector 0 and loads an application in sector 1. The problem however is that when I attempt to jump to the new function it appears to generate an exception (debugger goes to Dummy_Handler()).
Bootloader contains the following entries in map:
.application 0x00410000 0x0
0x00410000 . = ALIGN (0x4)
0x00410000 _sappl = .
0x00410004 _sjump = (. + 0x4)
The application image map file has:
.vectors 0x00410000 0xd0 src/ASF/sam/utils/cmsis/sam4s/source/templates/gcc/startup_sam4s.o
0x00410000 exception_table
…
.text.Reset_Handler
0x0041569c 0x100 src/ASF/sam/utils/cmsis/sam4s/source/templates/gcc/startup_sam4s.o
0x0041569c Reset_Handler
Exception table is defined as follows:
const DeviceVectors exception_table = {
/* Configure Initial Stack Pointer, using linker-generated symbols */
.pvStack = (void*) (&_estack),
.pfnReset_Handler = (void*) Reset_Handler,
The bootloader declares the application jump point as:
extern void (*_sjump) ();
and then makes the following call:
_sjump();
The memory contents at 0x00410004 are 0x0041569d, and I notice that this is not word aligned. Is this because we are using Thumb instructions? Either way why is it not 0x0041569c? Or more importantly why is this going to an exception?
Thanks,
Devan
Update:
Found this but it does not appear to work for me:
void (*user_code_entry)(void);
unsigned *p;
p = (uint32_t)&_sappl + 4;
user_code_entry = (void (*)(void))(*p - 1);
if(applGood && tempGood) {
SCB->VTOR = &_sappl;
PrintHex(p);
PrintHex(*p);
PrintHex(user_code_entry);
user_code_entry();
}
The code prints:
00410004
0041569D
0041569C
Update Update:
The code that attempted to jump with a C function pointer produced the following Disassembly:
--- D:\Zebra\PSPT_SAM4S\PSPT_SAM4S\SAM4S_PSPT\BOOTLOADER\Debug/.././BOOTLOADER.c
user_code_entry();
004005BA ldr r3, [r7, #4]
004005BC blx r3
I was able to get this working with the following assembly:
"mov r1, r0 \n"
"ldr r0, [r1, #4] \n"
"ldr sp, [r1] \n"
"blx r0"
Based on this I wonder if the stack reset is required and, if so, is it possible to accomplish such in C?
I had the same problem with SAM4E. I cannot guess what your problem might be, but I can point out difficulties that I had and information I used.
My bootloader was not storing in the correct memory location a few parts of the firmware. This was leading to the dummy_handler exception. When I fixed the error in the address calculations the bootloader worked perfectly.
My suggestions:
Follow ATMEL's example: the Document and the Example Code should be enough. The main.c is enough to understand how the bootloader should work. It is not necessary to get into partitioning details at the beginning.
You may want to read how you can execute functions/ISRs from RAM
This webpage explains the Intel HEX format.
Finally, after the bootloader finishes with the upgrade, you can read the flash and send it back to the host computer. Then compare it with the original image (using a script). That is how I debugged my bootloader.
Other ideas that might help:
Do you erase each page before you write it?
Do you unlock each memory space before you erase/write it?
You could lock the Bootloader's Section to avoid overwriting it by mistake
You could lock the section(s) of the upgraded firmware.
The address you have to point to is 0x00410000 not 0x00410004. The Atmel's example code (see function binary_exec) in combination with the Intel Hex format (record type 05) should solve this question.
I hope this piece of information will be of some help!
I had the same problem on the SAM4S which brought me to this question. So if someone arrives here again, here is what I found.
As ChrisB mentions following the Atmel example code is a good start, however I found that the problem was the actual code requesting the jump which just didn’t work for the SAM4S.
What was missing was rebasing the stack pointer before the vector table and then loading the reset handler address.
Try something like this:
static void ExecuteApp(void)
{
uint32_t i;
// Pointer to the Application Section
void (*application_code_entry)(void);
// -- Disable interrupts and system timer
__disable_irq();
SysTick->CTRL = 0; // Disable System timer
// disable and clear pending IRQs
for (i = 0; i < 8; i++)
{
NVIC->ICER[i] = 0xFFFFFFFF; // disable IRQ
NVIC->ICPR[i] = 0xFFFFFFFF; // clear pending IRQ
}
// Barriers
__DSB(); // data synchronization barrier
__ISB(); // instruction synchronization barrier
// Rebase the Stack Pointer
__set_MSP(*(uint32_t *) APPCODE_START_ADDR);
// Rebase the vector table base address
SCB->VTOR = ((uint32_t) APPCODE_START_ADDR & SCB_VTOR_TBLOFF_Msk);
// Load the Reset Handler address of the application
application_code_entry = (void (*)(void))(unsigned *)(*(unsigned *)
(APP_START_RESET_VEC_ADDRESS));
__DSB();
__ISB();
// -- Enable interrupts
__enable_irq();
// Jump to user Reset Handler in the application
application_code_entry();
}