Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * arch/ppc/boot/simple/relocate.S |
| 3 | * |
| 4 | * This is the common part of the loader relocation and initialization |
| 5 | * process. All of the board/processor specific initialization is |
| 6 | * done before we get here. |
| 7 | * |
| 8 | * Author: Tom Rini |
| 9 | * trini@mvista.com |
| 10 | * Derived from arch/ppc/boot/prep/head.S (Cort Dougan, many others). |
| 11 | * |
| 12 | * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under |
| 13 | * the terms of the GNU General Public License version 2. This program |
| 14 | * is licensed "as is" without any warranty of any kind, whether express |
| 15 | * or implied. |
| 16 | */ |
| 17 | |
| 18 | #include <linux/config.h> |
| 19 | #include <asm/cache.h> |
| 20 | #include <asm/ppc_asm.h> |
| 21 | |
| 22 | #define GETSYM(reg, sym) \ |
| 23 | lis reg, sym@h; ori reg, reg, sym@l |
| 24 | |
| 25 | .text |
| 26 | /* We get called from the early initialization code. |
| 27 | * Register 3 has the address where we were loaded, |
| 28 | * Register 4 contains any residual data passed from the |
| 29 | * boot rom. |
| 30 | */ |
| 31 | .globl relocate |
| 32 | relocate: |
| 33 | /* Save r3, r4 for later. |
| 34 | * The r8/r11 are legacy registers so I don't have to |
| 35 | * rewrite the code below :-). |
| 36 | */ |
| 37 | mr r8, r3 |
| 38 | mr r11, r4 |
| 39 | |
| 40 | /* compute the size of the whole image in words. */ |
| 41 | GETSYM(r4,start) |
| 42 | GETSYM(r5,end) |
| 43 | |
| 44 | addi r5,r5,3 /* round up */ |
| 45 | sub r5,r5,r4 /* end - start */ |
| 46 | srwi r5,r5,2 |
| 47 | mr r7,r5 /* Save for later use. */ |
| 48 | |
| 49 | /* |
| 50 | * Check if we need to relocate ourselves to the link addr or were |
| 51 | * we loaded there to begin with. |
| 52 | */ |
| 53 | cmpw cr0,r3,r4 |
| 54 | beq start_ldr /* If 0, we don't need to relocate */ |
| 55 | |
| 56 | /* Move this code somewhere safe. This is max(load + size, end) |
| 57 | * r8 == load address |
| 58 | */ |
| 59 | GETSYM(r4, start) |
| 60 | GETSYM(r5, end) |
| 61 | |
| 62 | sub r6,r5,r4 |
| 63 | add r6,r8,r6 /* r6 == phys(load + size) */ |
| 64 | |
| 65 | cmpw r5,r6 |
| 66 | bgt 1f |
| 67 | b 2f |
| 68 | 1: |
| 69 | mr r6, r5 |
| 70 | 2: |
| 71 | /* dest is in r6 */ |
| 72 | /* Ensure alignment --- this code is precautionary */ |
| 73 | addi r6,r6,4 |
| 74 | li r5,0x0003 |
| 75 | andc r6,r6,r5 |
| 76 | |
| 77 | /* Find physical address and size of do_relocate */ |
| 78 | GETSYM(r5, __relocate_start) |
| 79 | GETSYM(r4, __relocate_end) |
| 80 | GETSYM(r3, start) |
| 81 | |
| 82 | /* Size to copy */ |
| 83 | sub r4,r4,r5 |
| 84 | srwi r4,r4,2 |
| 85 | |
| 86 | /* Src addr to copy (= __relocate_start - start + where_loaded) */ |
| 87 | sub r3,r5,r3 |
| 88 | add r5,r8,r3 |
| 89 | |
| 90 | /* Save dest */ |
| 91 | mr r3, r6 |
| 92 | |
| 93 | /* Do the copy */ |
| 94 | mtctr r4 |
| 95 | 3: lwz r4,0(r5) |
| 96 | stw r4,0(r3) |
| 97 | addi r3,r3,4 |
| 98 | addi r5,r5,4 |
| 99 | bdnz 3b |
| 100 | |
| 101 | GETSYM(r4, __relocate_start) |
| 102 | GETSYM(r5, do_relocate) |
| 103 | |
| 104 | sub r4,r5,r4 /* Get entry point for do_relocate in */ |
| 105 | add r6,r6,r4 /* relocated section */ |
| 106 | |
| 107 | /* This will return to the relocated do_relocate */ |
| 108 | mtlr r6 |
| 109 | b flush_instruction_cache |
| 110 | |
| 111 | .section ".relocate_code","xa" |
| 112 | |
| 113 | do_relocate: |
| 114 | /* We have 2 cases --- start < load, or start > load |
| 115 | * This determines whether we copy from the end, or the start. |
| 116 | * Its easier to have 2 loops than to have paramaterised |
| 117 | * loops. Sigh. |
| 118 | */ |
| 119 | li r6,0 /* Clear checksum */ |
| 120 | mtctr r7 /* Setup for a loop */ |
| 121 | |
| 122 | GETSYM(r4, start) |
| 123 | mr r3,r8 /* Get the load addr */ |
| 124 | |
| 125 | cmpw cr0,r4,r3 /* If we need to copy from the end, do so */ |
| 126 | bgt do_relocate_from_end |
| 127 | |
| 128 | do_relocate_from_start: |
| 129 | 1: lwz r5,0(r3) /* Load and decrement */ |
| 130 | stw r5,0(r4) /* Store and decrement */ |
| 131 | addi r3,r3,4 |
| 132 | addi r4,r4,4 |
| 133 | xor r6,r6,r5 /* Update checksum */ |
| 134 | bdnz 1b /* Are we done? */ |
| 135 | b do_relocate_out /* Finished */ |
| 136 | |
| 137 | do_relocate_from_end: |
| 138 | GETSYM(r3, end) |
| 139 | slwi r4,r7,2 |
| 140 | add r4,r8,r4 /* Get the physical end */ |
| 141 | 1: lwzu r5,-4(r4) |
| 142 | stwu r5, -4(r3) |
| 143 | xor r6,r6,r5 |
| 144 | bdnz 1b |
| 145 | |
| 146 | do_relocate_out: |
| 147 | GETSYM(r3,start_ldr) |
| 148 | mtlr r3 /* Easiest way to do an absolute jump */ |
| 149 | /* Some boards don't boot up with the I-cache enabled. Do that |
| 150 | * now because the decompress runs much faster that way. |
| 151 | * As a side effect, we have to ensure the data cache is not enabled |
| 152 | * so we can access the serial I/O without trouble. |
| 153 | */ |
| 154 | b flush_instruction_cache |
| 155 | |
| 156 | .previous |
| 157 | |
| 158 | start_ldr: |
| 159 | /* Clear all of BSS and set up stack for C calls */ |
| 160 | lis r3,edata@h |
| 161 | ori r3,r3,edata@l |
| 162 | lis r4,end@h |
| 163 | ori r4,r4,end@l |
| 164 | subi r3,r3,4 |
| 165 | subi r4,r4,4 |
| 166 | li r0,0 |
| 167 | 50: stwu r0,4(r3) |
| 168 | cmpw cr0,r3,r4 |
| 169 | bne 50b |
| 170 | 90: mr r9,r1 /* Save old stack pointer (in case it matters) */ |
| 171 | lis r1,.stack@h |
| 172 | ori r1,r1,.stack@l |
| 173 | addi r1,r1,4096*2 |
| 174 | subi r1,r1,256 |
| 175 | li r2,0x000F /* Mask pointer to 16-byte boundary */ |
| 176 | andc r1,r1,r2 |
| 177 | |
| 178 | /* |
| 179 | * Exec kernel loader |
| 180 | */ |
| 181 | mr r3,r8 /* Load point */ |
| 182 | mr r4,r7 /* Program length */ |
| 183 | mr r5,r6 /* Checksum */ |
| 184 | mr r6,r11 /* Residual data */ |
| 185 | mr r7,r25 /* Validated OFW interface */ |
| 186 | bl load_kernel |
| 187 | |
| 188 | /* |
| 189 | * Make sure the kernel knows we don't have things set in |
| 190 | * registers. -- Tom |
| 191 | */ |
| 192 | li r4,0 |
| 193 | li r5,0 |
| 194 | li r6,0 |
| 195 | |
| 196 | /* |
| 197 | * Start at the begining. |
| 198 | */ |
| 199 | #ifdef CONFIG_PPC_MULTIPLATFORM |
| 200 | li r9,0xc |
| 201 | mtlr r9 |
| 202 | /* tell kernel we're prep, by putting 0xdeadc0de at KERNELLOAD, |
| 203 | * and tell the kernel to start on the 4th instruction since we |
| 204 | * overwrite the first 3 sometimes (which are 'nop'). |
| 205 | */ |
| 206 | lis r10,0xdeadc0de@h |
| 207 | ori r10,r10,0xdeadc0de@l |
| 208 | li r9,0 |
| 209 | stw r10,0(r9) |
| 210 | #else |
| 211 | li r9,0 |
| 212 | mtlr r9 |
| 213 | #endif |
| 214 | blr |
| 215 | |
| 216 | .comm .stack,4096*2,4 |