diff options
Diffstat (limited to 'arch/arm/cpu/setupc.S')
-rw-r--r-- | arch/arm/cpu/setupc.S | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/arch/arm/cpu/setupc.S b/arch/arm/cpu/setupc.S index d0de87dd9b..7fd5d012f0 100644 --- a/arch/arm/cpu/setupc.S +++ b/arch/arm/cpu/setupc.S @@ -1,4 +1,5 @@ #include <linux/linkage.h> +#include <asm/sections.h> .section .text.setupc @@ -35,3 +36,61 @@ ENTRY(setup_c) pop {r4, r5} mov pc, lr ENDPROC(setup_c) + +#ifdef CONFIG_RELOCATABLE +/* + * void relocate_to_adr(unsigned long targetadr) + * + * Copy binary to targetadr, relocate code, clear bss and continue + * executing at new address. + */ +.section .text.relocate_to_adr +ENTRY(relocate_to_adr) + /* r0: target address */ + push {r3, r4, r5, r6, r7, r8} + mov r7, lr + + mov r6, r0 + + bl get_runtime_offset + + mov r5, r0 + + ld_var _text, r0, r4 + mov r8, r0 + + sub r1, r0, r5 /* r1: from address */ + + cmp r1, r6 /* already at correct address? */ + beq 1f /* yes, skip copy to new address */ + + ld_var __bss_start, r2, r4 + + sub r2, r2, r0 /* r2: size */ + mov r0, r6 /* r0: target */ + + add r7, r7, r0 /* adjust return address */ + sub r7, r7, r1 /* lr += offset */ + + bl memcpy /* copy binary */ + +#ifdef CONFIG_MMU + bl arm_early_mmu_cache_flush +#endif + mov r0,#0 + mcr p15, 0, r0, c7, c5, 0 /* flush icache */ + + ldr r0,=1f + sub r0, r0, r8 + add r0, r0, r6 + mov pc, r0 /* jump to relocated address */ +1: + bl relocate_to_current_adr /* relocate binary */ + + mov lr, r7 + + pop {r3, r4, r5, r6, r7, r8} + mov pc, lr + +ENDPROC(relocate_to_adr) +#endif |