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.