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