3
votes

I'm building a bootloader for an application running on a stm32. The purpose of this is to be able to update the main application.

Since our software is pretty modular, my idea was to just configure a minimal version of it. All the initializations are the same, it jumps to a main function that contains all bootloader functionalities (checking if a new firmware is available on external flash, writing it to internal flash if that's the case) and in the end jumping to the actual application - which does the initialization all over again, but this time with additional peripherals, etc., eventually calling the real main.

The memory layout on the internal flash is like this

|0x08000000 boot loader
|----------------------
|0x08006000 application

bootloader main looks like this

extern void CallApplication(void);

int main(void) {
    printf("starting bootloader\n");

    printf("will jump to " TOSTRING(APP_START_ADDRESS) "\n");

    CallApplication();

    return 0;
}

where CallApplication is written in assembler

#define VTABLE_START_ADDRESS APP_START_ADDRESS
#define NVIC_VTABLE 0xE000ED08  // Vector Table Offset

    .globl CallApplication
    .thumb_func
CallApplication:
    // Set the application's vector table start address.
    movw    r0, #(VTABLE_START_ADDRESS & 0xffff)
    movt    r0, #(VTABLE_START_ADDRESS >> 16)
    movw    r1, #(NVIC_VTABLE & 0xffff)
    movt    r1, #(NVIC_VTABLE >> 16)
    str     r0, [r1]

    // Load the stack pointer from the application's vector table.
    ldr     sp, [r0]

    // Load the initial PC from the application's vector table and branch to
    // the application's entry point.
    ldr     r0, [r0, #4]
    bx      r0

This almost works - the 'real' application is called, does its initialization but eventually crashes for a yet unknown reason. What's interesting though is that the fault ISR of the bootloader (0x080022ae) is being called, not that of the real application (> 0x08006000) so something about setting the new vector table obviously failed.

2016-02-11 00:21:16,958 - INFO # init UART
2016-02-11 00:21:16,963 - INFO # Application:   boot_loader
2016-02-11 00:21:16,973 - INFO # -- init done, starting main --
2016-02-11 00:21:16,974 - INFO # starting bootloader
2016-02-11 00:21:16,976 - INFO # will jump to 0x8006000
2016-02-11 00:21:16,978 - INFO # init UART
2016-02-11 00:21:16,985 - INFO # Application:   hello_world
2016-02-11 00:21:17,797 - INFO # -- init done, starting main --
(hard fault led starts flashing)

What am I missing here?

The linker script for the main application defines

MEMORY
{
  FLASH (rx)      : ORIGIN = 0x08006000, LENGTH = 488K
  SRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
}

whereas the bootloader does

MEMORY
{
  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 24K
  SRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
}

the rest is shared

SECTIONS
{
    .text :
    {
        _text = .;

        /*
         * The vector table must be placed to the top of the
         * memory map. To achieve this, it was assigned to a 
         * special section called ".isr_vector"
         */
        KEEP(*(.isr_vector))

        /* followed by .text and .rodata: */
        *(.text*)
        *(.rodata*)

        _etext = .;
    } > FLASH

    /* Just to make sure that the contents does not exceed the flash size */
    . = ORIGIN(FLASH) + LENGTH(FLASH);


    /*
     * .data and .bss are placed into SRAM:
     */
    .data : AT(ADDR(.text) + SIZEOF(.text))
    {
        _data = .;
        *(.data*)
        _edata = .;
    } > SRAM

    .bss :
    {
        /* _bss and _ebss will be required during initialization */
        _bss = .;
        *(.bss*)
        _ebss = .;
    } > SRAM

    .aux : {
        . = ALIGN(4);
        *(.auxdata) /* .auxdata section */
        . = ALIGN(4);
    } > SRAM

    /* Just to make sure that the contents does not exceed the SRAM size */
    . = ORIGIN(SRAM) + LENGTH(SRAM);
}

Edit: I rewrote the section where VTOR is set in C to make it clearer for me what's going on, but I still end up in the bootloader's DefaultISR

printf("starting bootloader\n");

printf("will jump to " TOSTRING(APP_START_ADDRESS) "\n");

printf("before: %x\n", SCB->VTOR);

SCB->VTOR += APP_START_ADDRESS;

printf("after: %x\n", SCB->VTOR);

asm volatile("mov r0, #0x6000");
asm volatile("ldr sp, [r0]");
asm volatile("ldr r0, [r0, #4]");
asm volatile("bx r0");

outputs

2016-02-11 23:49:31,833 - INFO # starting bootloader
2016-02-11 23:49:31,835 - INFO # will jump to 0x6000
2016-02-11 23:49:31,836 - INFO # before: 8000000
2016-02-11 23:49:31,837 - INFO # after: 8006000
2016-02-11 23:49:31,839 - INFO # init UART
2016-02-11 23:49:31,841 - INFO # …
2
That is hard to debuge remotely. where does the exception occur? Use a debugger and step through your code.too honest for this site
It crashes the moment I enable interrupts by calling __set_BASEPRI(0)user1273684
Man! Which exception?, check the SCS registers, status, address, etc. If you don't know what I'm talking about, read the manuals, namely the architecture reference manual, but also others. Cortex-M4 is not PIC16 or AVR.too honest for this site
Seems like I was mistaken, it doesn't crash, it just calls the default interrupt handler - the one of the bootloader still. So setting the new VTABLE didn't work.user1273684
Note: Use standard names. The vector register e.g. is VTOR. Sorry, I cannot help you further. Just one info: It is not the STM's fault! For me it works.too honest for this site

2 Answers

3
votes

In my case is a STM32L Cortex-M3, but I think it works in the same way.

In the bootloader, after disabling all sources of interrupt (not masking them), I do the following:

#define APP_LOCATION 0x08006000

typedef void (*pFunction)(void);
pFunction jump;
volatile uint32_t jumpAddress;
register uint32_t regMainStackPointer __ASM("msp");

void Jump( void ) {
    jumpAddress = *( volatile uint32_t* )( APP_LOCATION + 4 );
    jump = ( pFunction )jumpAddress;
    mainStackPointer = *( volatile uint32_t* )APP_LOCATION;
    jump();
}

And in the application itself, the first thing to do before enabling any interrupt is:

SCB->VTOR = 0x0x08006000;

The linkers here are equal.

I noticed something strange in your code:

SCB->VTOR += APP_START_ADDRESS;

If APP_START_ADDRESS contains the address (0x08006000) instead of the offset (0x6000), the resulting value in VTOR will be 0x08000000 + 0x08006000, perhaps the problem is here?

It might help if you show some code from the application. Hope it helps.

2
votes

the CPU init function from the HAL was doing

  /* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif

and by that overwriting my setting to SCB->VTOR.

It works when this is removed, no further magic required.