cassemblyarmbootloadernrf51

In house bootloader ARM cortex M4 NRF52 chip


I am working on making a bootloader for a side project.

I have read in a hex file, verified the checksum and stored everything in flash with a corresponding address with an offset of 0x4000. I am having issues jumping to my application. I have read, searched and tried alot of different things such as the code here.

http://www.keil.com/support/docs/3913.htm

my current code is this;

int binary_exec(void * Address){
    int i;
    __disable_irq();
// 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)0x4000 & 0x1ffff80);
// Barriars
__DSB();
__ISB();

__enable_irq();

// -- Load Stack & PC
binExec(Address);
return 0;
}

__asm void binexec(uint32_t *address)
{
    mov   r1, r0    
   ldr   r0, [r1, #4] 
   ldr   sp, [r1]     
   blx   r0"

}

This just jumps to a random location and does not do anything. I have manually added the address to the PC using keil's register window and it jumps straight to my application but I have not found a way to do it using code. Any ideas? Thank you in advance.

Also the second to last line of the hex file there is the start linear address record: http://www.keil.com/support/docs/1584.htm

does anyone know what to do with this line?

Thank you,

Eric Micallef


Solution

  • This is what I am talking about can you show us some fragments that look like this, this is an entire application just doesnt do much...

    20004000 <_start>:
    20004000:   20008000
    20004004:   20004049
    20004008:   2000404f
    2000400c:   2000404f
    20004010:   2000404f
    20004014:   2000404f
    20004018:   2000404f
    2000401c:   2000404f
    20004020:   2000404f
    20004024:   2000404f
    20004028:   2000404f
    2000402c:   2000404f
    20004030:   2000404f
    20004034:   2000404f
    20004038:   2000404f
    2000403c:   20004055
    20004040:   2000404f
    20004044:   2000404f
    
    20004048 <reset>:
    20004048:   f000 f806   bl  20004058 <notmain>
    2000404c:   e7ff        b.n 2000404e <hang>
    
    2000404e <hang>:
    2000404e:   e7fe        b.n 2000404e <hang>
    
    20004050 <dummy>:
    20004050:   4770        bx  lr
        ...
    
    20004054 <systick_handler>:
    20004054:   4770        bx  lr
    20004056:   bf00        nop
    
    20004058 <notmain>:
    20004058:   b510        push    {r4, lr}
    2000405a:   2400        movs    r4, #0
    2000405c:   4620        mov r0, r4
    2000405e:   3401        adds    r4, #1
    20004060:   f7ff fff6   bl  20004050 <dummy>
    20004064:   2c64        cmp r4, #100    ; 0x64
    20004066:   d1f9        bne.n   2000405c <notmain+0x4>
    20004068:   2000        movs    r0, #0
    2000406a:   bd10        pop {r4, pc}
    

    offset 0x00 is the stack pointer

    20004000:   20008000
    

    offset 0x04 is the reset vector or the entry point to this program

    20004004:   20004049
    

    I filled in the unused ones so they land in an infinite loop

    20004008:   2000404f
    

    and tossed in a different one just to show

    2000403c:   20004055
    

    In this case the VTOR would be set to 0x2004000 I would read 0x20004049 from 0x20004004 and then BX to that address.

    so my binexec would be fed the address 0x20004000 and I would do something like this

    ldr r1,[r0]
    mov sp,r1
    ldr r2,[r0,#4]
    bx r2
    

    If I wanted to fake a reset into that code. a thumb approach with thumb2 I assume you can ldr sp,[r0], I dont hand code thumb2 so dont have those memorized, and there are different thumb2 sets of extensions, as well as different syntax options in gas.

    Now if you were not going to support interrupts, or for other reasons (might carry some binary code in your flash that you want to perform better and you copy that from flash to ram then use it in ram) you could download to ram an application that simply has its first instruction at the entry point, no vector table:

    20004000 <_start>:
    20004000:   f000 f804   bl  2000400c <notmain>
    20004004:   e7ff        b.n 20004006 <hang>
    
    20004006 <hang>:
    20004006:   e7fe        b.n 20004006 <hang>
    
    20004008 <dummy>:
    20004008:   4770        bx  lr
        ...
    
    2000400c <notmain>:
    2000400c:   b510        push    {r4, lr}
    2000400e:   2400        movs    r4, #0
    20004010:   4620        mov r0, r4
    20004012:   3401        adds    r4, #1
    20004014:   f7ff fff8   bl  20004008 <dummy>
    20004018:   2c64        cmp r4, #100    ; 0x64
    2000401a:   d1f9        bne.n   20004010 <notmain+0x4>
    2000401c:   2000        movs    r0, #0
    2000401e:   bd10        pop {r4, pc}
    

    In this case it would need to be agreed that the downloaded program is built for 0x20004000, you would download the data to that address, but when you want to run it you would instead do this

    .globl binexec
    binexec:
      bx r0
    

    in C

    binexec(0x20004000|1);
    

    or

    .globl binexec
    binexec:
      orr r0,#1
      bx r0
    

    just to be safe(r).

    In both cases you need to build your binaries right if you want them to run, both have to be linked for the target address, in particular the vector table approach, thus the question, can you show us an example vector table from one of your downloaded, programs, even the first few words might suffice...