1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * Copyright (C) Paul Mackerras 1997.
4  *
5  * Adapted for 64 bit LE PowerPC by Andrew Tauferner
6  */
7 
8 #include "ppc_asm.h"
9 
10 RELA = 7
11 RELASZ = 8
12 RELAENT = 9
13 
14 	.data
15 	/* A procedure descriptor used when booting this as a COFF file.
16 	 * When making COFF, this comes first in the link and we're
17 	 * linked at 0x500000.
18 	 */
19 	.globl	_zimage_start_opd
20 _zimage_start_opd:
21 	.long	0x500000, 0, 0, 0
22 	.text
23 	b	_zimage_start
24 
25 #ifdef __powerpc64__
26 .balign 8
27 p_start:	.8byte	_start
28 p_etext:	.8byte	_etext
29 p_bss_start:	.8byte	__bss_start
30 p_end:		.8byte	_end
31 
32 p_toc:		.8byte	.TOC. - p_base
33 p_dyn:		.8byte	__dynamic_start - p_base
34 p_rela:		.8byte	__rela_dyn_start - p_base
35 p_prom:		.8byte	0
36 	.weak	_platform_stack_top
37 p_pstack:	.8byte	_platform_stack_top
38 #else
39 p_start:	.long	_start
40 p_etext:	.long	_etext
41 p_bss_start:	.long	__bss_start
42 p_end:		.long	_end
43 
44 	.weak	_platform_stack_top
45 p_pstack:	.long	_platform_stack_top
46 #endif
47 
48 	.weak	_zimage_start
49 _zimage_start:
50 	.globl	_zimage_start_lib
51 _zimage_start_lib:
52 	/* Work out the offset between the address we were linked at
53 	   and the address where we're running. */
54 	bcl	20,31,.+4
55 p_base:	mflr	r10		/* r10 now points to runtime addr of p_base */
56 #ifndef __powerpc64__
57 	/* grab the link address of the dynamic section in r11 */
58 	addis	r11,r10,(_GLOBAL_OFFSET_TABLE_-p_base)@ha
59 	lwz	r11,(_GLOBAL_OFFSET_TABLE_-p_base)@l(r11)
60 	cmpwi	r11,0
61 	beq	3f		/* if not linked -pie */
62 	/* get the runtime address of the dynamic section in r12 */
63 	.weak	__dynamic_start
64 	addis	r12,r10,(__dynamic_start-p_base)@ha
65 	addi	r12,r12,(__dynamic_start-p_base)@l
66 	subf	r11,r11,r12	/* runtime - linktime offset */
67 
68 	/* The dynamic section contains a series of tagged entries.
69 	 * We need the RELA and RELACOUNT entries. */
70 	li	r9,0
71 	li	r0,0
72 9:	lwz	r8,0(r12)	/* get tag */
73 	cmpwi	r8,0
74 	beq	10f		/* end of list */
75 	cmpwi	r8,RELA
76 	bne	11f
77 	lwz	r9,4(r12)	/* get RELA pointer in r9 */
78 	b	12f
79 11:	cmpwi	r8,RELASZ
80 	bne	.Lcheck_for_relaent
81 	lwz	r0,4(r12)       /* get RELASZ value in r0 */
82 	b	12f
83 .Lcheck_for_relaent:
84 	cmpwi	r8,RELAENT
85 	bne	12f
86 	lwz     r14,4(r12)      /* get RELAENT value in r14 */
87 12:	addi	r12,r12,8
88 	b	9b
89 
90 	/* The relocation section contains a list of relocations.
91 	 * We now do the R_PPC_RELATIVE ones, which point to words
92 	 * which need to be initialized with addend + offset */
93 10:	/* skip relocation if we don't have both */
94 	cmpwi	r0,0
95 	beq	3f
96 	cmpwi	r9,0
97 	beq	3f
98 	cmpwi	r14,0
99 	beq	3f
100 
101 	add	r9,r9,r11	/* Relocate RELA pointer */
102 	divwu   r0,r0,r14       /* RELASZ / RELAENT */
103 	mtctr	r0
104 2:	lbz	r0,4+3(r9)	/* ELF32_R_INFO(reloc->r_info) */
105 	cmpwi	r0,22		/* R_PPC_RELATIVE */
106 	bne	.Lnext
107 	lwz	r12,0(r9)	/* reloc->r_offset */
108 	lwz	r0,8(r9)	/* reloc->r_addend */
109 	add	r0,r0,r11
110 	stwx	r0,r11,r12
111 .Lnext:	add	r9,r9,r14
112 	bdnz	2b
113 
114 	/* Do a cache flush for our text, in case the loader didn't */
115 3:	lwz	r9,p_start-p_base(r10)	/* note: these are relocated now */
116 	lwz	r8,p_etext-p_base(r10)
117 4:	dcbf	r0,r9
118 	icbi	r0,r9
119 	addi	r9,r9,0x20
120 	cmplw	cr0,r9,r8
121 	blt	4b
122 	sync
123 	isync
124 
125 	/* Clear the BSS */
126 	lwz	r9,p_bss_start-p_base(r10)
127 	lwz	r8,p_end-p_base(r10)
128 	li	r0,0
129 5:	stw	r0,0(r9)
130 	addi	r9,r9,4
131 	cmplw	cr0,r9,r8
132 	blt	5b
133 
134 	/* Possibly set up a custom stack */
135 	lwz	r8,p_pstack-p_base(r10)
136 	cmpwi	r8,0
137 	beq	6f
138 	lwz	r1,0(r8)
139 	li	r0,0
140 	stwu	r0,-16(r1)	/* establish a stack frame */
141 6:
142 #else /* __powerpc64__ */
143 	/* Save the prom pointer at p_prom. */
144 	std	r5,(p_prom-p_base)(r10)
145 
146 	/* Set r2 to the TOC. */
147 	ld	r2,(p_toc-p_base)(r10)
148 	add	r2,r2,r10
149 
150 	/* Grab the link address of the dynamic section in r11. */
151 	ld	r11,-32768(r2)
152 	cmpwi	r11,0
153 	beq	3f              /* if not linked -pie then no dynamic section */
154 
155 	ld	r11,(p_dyn-p_base)(r10)
156 	add	r11,r11,r10
157 	ld	r9,(p_rela-p_base)(r10)
158 	add	r9,r9,r10
159 
160 	li	r13,0
161 	li	r8,0
162 9:	ld	r12,0(r11)       /* get tag */
163 	cmpdi	r12,0
164 	beq	12f              /* end of list */
165 	cmpdi	r12,RELA
166 	bne	10f
167 	ld	r13,8(r11)       /* get RELA pointer in r13 */
168 	b	11f
169 10:	cmpwi   r12,RELASZ
170 	bne	.Lcheck_for_relaent
171 	lwz	r8,8(r11)	/* get RELASZ pointer in r8 */
172 	b	11f
173 .Lcheck_for_relaent:
174 	cmpwi	r12,RELAENT
175 	bne     11f
176 	lwz     r14,8(r11)      /* get RELAENT pointer in r14 */
177 11:	addi	r11,r11,16
178 	b	9b
179 12:
180 	cmpdi	r13,0            /* check we have both RELA, RELASZ, RELAENT*/
181 	cmpdi	cr1,r8,0
182 	beq	3f
183 	beq	cr1,3f
184 	cmpdi	r14,0
185 	beq	3f
186 
187 	/* Calcuate the runtime offset. */
188 	subf	r13,r13,r9
189 
190 	/* Run through the list of relocations and process the
191 	 * R_PPC64_RELATIVE ones. */
192 	divdu   r8,r8,r14       /* RELASZ / RELAENT */
193 	mtctr	r8
194 13:	ld	r0,8(r9)        /* ELF64_R_TYPE(reloc->r_info) */
195 	cmpdi	r0,22           /* R_PPC64_RELATIVE */
196 	bne	.Lnext
197 	ld	r12,0(r9)        /* reloc->r_offset */
198 	ld	r0,16(r9)       /* reloc->r_addend */
199 	add	r0,r0,r13
200 	stdx	r0,r13,r12
201 .Lnext:	add	r9,r9,r14
202 	bdnz	13b
203 
204 	/* Do a cache flush for our text, in case the loader didn't */
205 3:	ld	r9,p_start-p_base(r10)	/* note: these are relocated now */
206 	ld	r8,p_etext-p_base(r10)
207 4:	dcbf	r0,r9
208 	icbi	r0,r9
209 	addi	r9,r9,0x20
210 	cmpld	cr0,r9,r8
211 	blt	4b
212 	sync
213 	isync
214 
215 	/* Clear the BSS */
216 	ld	r9,p_bss_start-p_base(r10)
217 	ld	r8,p_end-p_base(r10)
218 	li	r0,0
219 5:	std	r0,0(r9)
220 	addi	r9,r9,8
221 	cmpld	cr0,r9,r8
222 	blt	5b
223 
224 	/* Possibly set up a custom stack */
225 	ld	r8,p_pstack-p_base(r10)
226 	cmpdi	r8,0
227 	beq	6f
228 	ld	r1,0(r8)
229 	li	r0,0
230 	stdu	r0,-112(r1)	/* establish a stack frame */
231 6:
232 #endif  /* __powerpc64__ */
233 	/* Call platform_init() */
234 	bl	platform_init
235 
236 	/* Call start */
237 	b	start
238 
239 #ifdef __powerpc64__
240 
241 #define PROM_FRAME_SIZE 512
242 
243 .macro OP_REGS op, width, start, end, base, offset
244 	.Lreg=\start
245 	.rept (\end - \start + 1)
246 	\op	.Lreg,\offset+\width*.Lreg(\base)
247 	.Lreg=.Lreg+1
248 	.endr
249 .endm
250 
251 #define SAVE_GPRS(start, end, base)	OP_REGS std, 8, start, end, base, 0
252 #define REST_GPRS(start, end, base)	OP_REGS ld, 8, start, end, base, 0
253 #define SAVE_GPR(n, base)		SAVE_GPRS(n, n, base)
254 #define REST_GPR(n, base)		REST_GPRS(n, n, base)
255 
256 /* prom handles the jump into and return from firmware.  The prom args pointer
257    is loaded in r3. */
258 .globl prom
259 prom:
260 	mflr	r0
261 	std	r0,16(r1)
262 	stdu	r1,-PROM_FRAME_SIZE(r1) /* Save SP and create stack space */
263 
264 	SAVE_GPR(2, r1)
265 	SAVE_GPRS(13, 31, r1)
266 	mfcr    r10
267 	std     r10,8*32(r1)
268 	mfmsr   r10
269 	std     r10,8*33(r1)
270 
271 	/* remove MSR_LE from msr but keep MSR_SF */
272 	mfmsr	r10
273 	rldicr	r10,r10,0,62
274 	mtsrr1	r10
275 
276 	/* Load FW address, set LR to label 1, and jump to FW */
277 	bcl	20,31,0f
278 0:	mflr	r10
279 	addi	r11,r10,(1f-0b)
280 	mtlr	r11
281 
282 	ld	r10,(p_prom-0b)(r10)
283 	mtsrr0	r10
284 
285 	rfid
286 
287 1:	/* Return from OF */
288 	FIXUP_ENDIAN
289 
290 	/* Restore registers and return. */
291 	rldicl  r1,r1,0,32
292 
293 	/* Restore the MSR (back to 64 bits) */
294 	ld      r10,8*(33)(r1)
295 	mtmsr	r10
296 	isync
297 
298 	/* Restore other registers */
299 	REST_GPR(2, r1)
300 	REST_GPRS(13, 31, r1)
301 	ld      r10,8*32(r1)
302 	mtcr	r10
303 
304 	addi    r1,r1,PROM_FRAME_SIZE
305 	ld      r0,16(r1)
306 	mtlr    r0
307 	blr
308 #endif
309