summaryrefslogtreecommitdiffstats
path: root/arch/ppc/boot/simple/relocate.S
blob: 2533113c1cc50d94cc8c604f672c8a536efdab69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*
 * This is the common part of the loader relocation and initialization
 * process.  All of the board/processor specific initialization is
 * done before we get here.
 *
 * Author: Tom Rini
 *	   trini@mvista.com
 * Derived from arch/ppc/boot/prep/head.S (Cort Dougan, many others).
 *
 * 2001-2004 (c) MontaVista, Software, Inc.  This file is licensed under
 * the terms of the GNU General Public License version 2.  This program
 * is licensed "as is" without any warranty of any kind, whether express
 * or implied.
 */

#include <linux/config.h>
#include <asm/cache.h>
#include <asm/ppc_asm.h>

#define GETSYM(reg, sym)	\
	lis	reg, sym@h; ori	reg, reg, sym@l

	.text
	/* We get called from the early initialization code.
	 * Register 3 has the address where we were loaded,
	 * Register 4 contains any residual data passed from the
	 * boot rom.
	 */
	.globl	relocate
relocate:
	/* Save r3, r4 for later.
	 * The r8/r11 are legacy registers so I don't have to
	 * rewrite the code below :-).
	 */
	mr	r8, r3
	mr	r11, r4

	/* compute the size of the whole image in words. */
	GETSYM(r4,start)
	GETSYM(r5,end)

	addi	r5,r5,3		/* round up */
	sub	r5,r5,r4	/* end - start */
	srwi	r5,r5,2
	mr	r7,r5		/* Save for later use. */

	/*
	 * Check if we need to relocate ourselves to the link addr or were
	 * we loaded there to begin with.
	 */
	cmpw	cr0,r3,r4
	beq	start_ldr	/* If 0, we don't need to relocate */

	/* Move this code somewhere safe.  This is max(load + size, end)
	 * r8 == load address
	 */
	GETSYM(r4, start)
	GETSYM(r5, end)

	sub	r6,r5,r4
	add	r6,r8,r6	/* r6 == phys(load + size) */

	cmpw	r5,r6
	bgt	1f
	b	2f
1:
	mr	r6, r5
2:
	/* dest is in r6 */
	/* Ensure alignment --- this code is precautionary */
	addi	r6,r6,4
	li	r5,0x0003
	andc	r6,r6,r5

	/* Find physical address and size of do_relocate */
	GETSYM(r5, __relocate_start)
	GETSYM(r4, __relocate_end)
	GETSYM(r3, start)

	/* Size to copy */
	sub	r4,r4,r5
	srwi	r4,r4,2

	/* Src addr to copy (= __relocate_start - start + where_loaded) */
	sub	r3,r5,r3
	add	r5,r8,r3

	/* Save dest */
	mr	r3, r6

	/* Do the copy */
	mtctr	r4
3:	lwz	r4,0(r5)
	stw	r4,0(r3)
	addi	r3,r3,4
	addi	r5,r5,4
	bdnz	3b

	GETSYM(r4, __relocate_start)
	GETSYM(r5, do_relocate)

	sub	r4,r5,r4	/* Get entry point for do_relocate in */
	add	r6,r6,r4	/* relocated section */

	/* This will return to the relocated do_relocate */
	mtlr	r6
	b	flush_instruction_cache

	.section ".relocate_code","xa"
	
do_relocate:
	/* We have 2 cases --- start < load, or start > load
	 * This determines whether we copy from the end, or the start.
	 * Its easier to have 2 loops than to have paramaterised
	 * loops.  Sigh.
	 */
	li	r6,0		/* Clear checksum */
	mtctr	r7		/* Setup for a loop */
	
	GETSYM(r4, start)
	mr	r3,r8		/* Get the load addr */

	cmpw	cr0,r4,r3	/* If we need to copy from the end, do so */
	bgt	do_relocate_from_end

do_relocate_from_start:
1:	lwz	r5,0(r3)	/* Load and decrement */
	stw	r5,0(r4)	/* Store and decrement */
	addi	r3,r3,4
	addi	r4,r4,4
	xor	r6,r6,r5	/* Update checksum */
	bdnz	1b		/* Are we done? */
	b	do_relocate_out	/* Finished */

do_relocate_from_end:
	GETSYM(r3, end)
	slwi	r4,r7,2
	add	r4,r8,r4	/* Get the physical end */
1:	lwzu	r5,-4(r4)
	stwu	r5, -4(r3)
	xor	r6,r6,r5
	bdnz	1b

do_relocate_out:
	GETSYM(r3,start_ldr)
	mtlr	r3		/* Easiest way to do an absolute jump */
/* Some boards don't boot up with the I-cache enabled.  Do that
 * now because the decompress runs much faster that way.
 * As a side effect, we have to ensure the data cache is not enabled
 * so we can access the serial I/O without trouble.
 */
	b	flush_instruction_cache

	.previous

start_ldr:
/* Clear all of BSS and set up stack for C calls */
	lis	r3,edata@h
	ori	r3,r3,edata@l
	lis	r4,end@h
	ori	r4,r4,end@l
	subi	r3,r3,4
	subi	r4,r4,4
	li	r0,0
50:	stwu	r0,4(r3)
	cmpw	cr0,r3,r4
	bne	50b
90:	mr	r9,r1		/* Save old stack pointer (in case it matters) */
	lis	r1,.stack@h
	ori	r1,r1,.stack@l
	addi	r1,r1,4096*2
	subi	r1,r1,256
	li	r2,0x000F	/* Mask pointer to 16-byte boundary */
	andc	r1,r1,r2

	/*
	 * Exec kernel loader
	 */
	mr	r3,r8		/* Load point */
	mr	r4,r7		/* Program length */
	mr	r5,r6		/* Checksum */
	mr	r6,r11		/* Residual data */
	mr	r7,r25		/* Validated OFW interface */
	bl	load_kernel

	/*
	 * Make sure the kernel knows we don't have things set in
	 * registers.  -- Tom
	 */
	li	r4,0
	li	r5,0
	li	r6,0

	/*
	 * Start at the begining.
	 */
#ifdef CONFIG_PPC_PREP
	li	r9,0xc
	mtlr	r9
	/* tell kernel we're prep, by putting 0xdeadc0de at KERNELLOAD,
	 * and tell the kernel to start on the 4th instruction since we
	 * overwrite the first 3 sometimes (which are 'nop').
	 */
	lis	r10,0xdeadc0de@h
	ori	r10,r10,0xdeadc0de@l
	li	r9,0
	stw	r10,0(r9)
#else
	li	r9,0
	mtlr	r9
#endif
	blr

	.comm	.stack,4096*2,4