Uboot Code Reloacation

Table of Contents

1 Why to relocate code

We known that code always be stored in flash in embedded system, there are three flash type: spi non-flash, nor-flash and nand flash, code can be run in non-flash directly, but we dont't run code in the flash directly for performance, need to copy the code from flash to ram to execute it, that why it is need to relocate code in uboot.

2 How to complete the relocation

we just use arm926ejs as example, the start.S file as following:

#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
.globl _start
_start:
.globl _NOR_BOOT_CFG
_NOR_BOOT_CFG:
        .word   CONFIG_SYS_DV_NOR_BOOT_CFG
        b       reset
#else
.globl _start
_start:
        b       reset
#endif
#ifdef CONFIG_SPL_BUILD
/* No exception handlers in preloader */
        ldr     pc, _hang
        ldr     pc, _hang
        ldr     pc, _hang
        ldr     pc, _hang
        ldr     pc, _hang
        ldr     pc, _hang
        ldr     pc, _hang

_hang:
        .word   do_hang
/* pad to 64 byte boundary */
        .word   0x12345678
        .word   0x12345678
        .word   0x12345678
        .word   0x12345678
        .word   0x12345678
        .word   0x12345678
        .word   0x12345678
#else
        ldr     pc, _undefined_instruction
        ldr     pc, _software_interrupt
        ldr     pc, _prefetch_abort
        ldr     pc, _data_abort
        ldr     pc, _not_used
        ldr     pc, _irq
        ldr     pc, _fiq

_undefined_instruction:
        .word undefined_instruction
_software_interrupt:
        .word software_interrupt
_prefetch_abort:
        .word prefetch_abort
_data_abort:
        .word data_abort
_not_used:
        .word not_used
_irq:
        .word irq
_fiq:
        .word fiq

#endif  /* CONFIG_SPL_BUILD */
        .balignl 16,0xdeadbeef


/*
 *************************************************************************
 *
 * Startup Code (reset vector)
 *
 * do important init only if we don't start from memory!
 * setup Memory and board specific bits prior to relocation.
 * relocate armboot to ram
 * setup stack
 *
 *************************************************************************
 */

.globl _TEXT_BASE
_TEXT_BASE:
#ifdef CONFIG_NAND_SPL /* deprecated, use instead CONFIG_SPL_BUILD */
        .word   CONFIG_SYS_TEXT_BASE
#else
#ifdef CONFIG_SPL_BUILD
        .word   CONFIG_SPL_TEXT_BASE
#else
        .word   CONFIG_SYS_TEXT_BASE
#endif
#endif

/*
 * These are defined in the board-specific linker script.
 * Subtracting _start from them lets the linker put their
 * relative position in the executable instead of leaving
 * them null.
 */
.globl _bss_start_ofs
_bss_start_ofs:
        .word __bss_start - _start

.globl _bss_end_ofs
_bss_end_ofs:
        .word __bss_end__ - _start

.globl _end_ofs
_end_ofs:
        .word _end - _start

#ifdef CONFIG_NAND_U_BOOT
.globl _end
_end:
        .word __bss_end__
#endif

#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
        .word   0x0badc0de

/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
        .word 0x0badc0de
#endif

/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
        .word   0x0badc0de

/*
 * the actual reset code
 */

reset:
        /*
         * set the cpu to SVC32 mode
         */
        mrs     r0,cpsr
        bic     r0,r0,#0x1f
        orr     r0,r0,#0xd3
        msr     cpsr,r0

        /*
         * we do sys-critical inits only at reboot,
         * not when booting from ram!
         */
        bl      cpu_init_crit

/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
#ifdef CONFIG_NAND_SPL /* deprecated, use instead CONFIG_SPL_BUILD */
        ldr     sp, =(CONFIG_SYS_INIT_SP_ADDR)
#else
#ifdef CONFIG_SPL_BUILD
        ldr     sp, =(CONFIG_SPL_STACK)
#else
        ldr     sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
#endif
        bic     sp, sp, #7 /* 8-byte alignment for ABI compliance */
        ldr     r0,=0x00000000
        bl      board_init_f

/*------------------------------------------------------------------------------*/

/*
 * void relocate_code (addr_sp, gd, addr_moni)
 *
 * This "function" does not return, instead it continues in RAM
 * after relocating the monitor code.
 *
 */
        .globl  relocate_code
relocate_code:
        mov     r4, r0  /* save addr_sp */
        mov     r5, r1  /* save addr of gd */
        mov     r6, r2  /* save addr of destination */

        /* Set up the stack                                                 */
stack_setup:
        mov     sp, r4

        adr     r0, _start
        sub     r9, r6, r0              /* r9 <- relocation offset */
        cmp     r0, r6
        beq     clear_bss               /* skip relocation */
        mov     r1, r6                  /* r1 <- scratch for copy loop */
        ldr     r3, _bss_start_ofs
        add     r2, r0, r3              /* r2 <- source end address         */

copy_loop:
        ldmia   r0!, {r9-r10}           /* copy from source address [r0]    */
        stmia   r1!, {r9-r10}           /* copy to   target address [r1]    */
        cmp     r0, r2                  /* until source end address [r2]    */
        blo     copy_loop

#ifndef CONFIG_SPL_BUILD
        /*
         * fix .rel.dyn relocations
         */
        ldr     r0, _TEXT_BASE          /* r0 <- Text base */
        sub     r9, r6, r0              /* r9 <- relocation offset */
        ldr     r10, _dynsym_start_ofs  /* r10 <- sym table ofs */
        add     r10, r10, r0            /* r10 <- sym table in FLASH */
        ldr     r2, _rel_dyn_start_ofs  /* r2 <- rel dyn start ofs */
        add     r2, r2, r0              /* r2 <- rel dyn start in FLASH */
        ldr     r3, _rel_dyn_end_ofs    /* r3 <- rel dyn end ofs */
        add     r3, r3, r0              /* r3 <- rel dyn end in FLASH */
fixloop:
        ldr     r0, [r2]                /* r0 <- location to fix up, IN FLASH! */
        add     r0, r0, r9              /* r0 <- location to fix up in RAM */
        ldr     r1, [r2, #4]
        and     r7, r1, #0xff
        cmp     r7, #23                 /* relative fixup? */
        beq     fixrel
        cmp     r7, #2                  /* absolute fixup? */
        beq     fixabs
        /* ignore unknown type of fixup */
        b       fixnext
fixabs:
        /* absolute fix: set location to (offset) symbol value */
        mov     r1, r1, LSR #4          /* r1 <- symbol index in .dynsym */
        add     r1, r10, r1             /* r1 <- address of symbol in table */
        ldr     r1, [r1, #4]            /* r1 <- symbol value */
        add     r1, r1, r9              /* r1 <- relocated sym addr */
        b       fixnext
fixrel:
        /* relative fix: increase location by offset */
        ldr     r1, [r0]
        add     r1, r1, r9
fixnext:
        str     r1, [r0]
        add     r2, r2, #8              /* each rel.dyn entry is 8 bytes */
        cmp     r2, r3
        blo     fixloop
#endif

clear_bss:
#ifdef CONFIG_SPL_BUILD
        /* No relocation for SPL */
        ldr     r0, =__bss_start
        ldr     r1, =__bss_end__
#else
        ldr     r0, _bss_start_ofs
        ldr     r1, _bss_end_ofs
        mov     r4, r6                  /* reloc addr */
        add     r0, r0, r4
        add     r1, r1, r4
#endif
        mov     r2, #0x00000000         /* clear                            */

clbss_l:cmp     r0, r1                  /* clear loop... */
        bhs     clbss_e                 /* if reached end of bss, exit */
        str     r2, [r0]
        add     r0, r0, #4
        b       clbss_l
clbss_e:

#ifndef CONFIG_SPL_BUILD
        bl coloured_LED_init
        bl red_led_on
#endif

/*
 * We are done. Do not return, instead branch to second part of board
 * initialization, now running from RAM.
 */
#ifdef CONFIG_NAND_SPL
        ldr     r0, _nand_boot_ofs
        mov     pc, r0

_nand_boot_ofs:
        .word nand_boot
#else
        ldr     r0, _board_init_r_ofs
        ldr     r1, _TEXT_BASE
        add     lr, r0, r1
        add     lr, lr, r9
        /* setup parameters for board_init_r */
        mov     r0, r5          /* gd_t */
        mov     r1, r6          /* dest_addr */
        /* jump to it ... */
        mov     pc, lr

_board_init_r_ofs:
        .word board_init_r - _start
#endif

_rel_dyn_start_ofs:
        .word __rel_dyn_start - _start
_rel_dyn_end_ofs:
        .word __rel_dyn_end - _start
_dynsym_start_ofs:
        .word __dynsym_start - _start

in the arm command collection, there are two type jusp command, one type is short jump, just like b, bl. another type is long jump command, using mov pc rx can jump to anywhere.
in above code, firstly, it is using "ldr r0, _board_init_r_ofs" to get the offset to start address, then using "add lr, lr, r9" to get the address will jump to, finally, using "mov pc, lr" jump to the address.

Date: 2012-08-25 六

Author: yannik

Org version 7.8.11 with Emacs version 24

Validate XHTML 1.0