xref: /openbmc/linux/arch/powerpc/boot/crt0.S (revision cd99dac6)
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
10RELA = 7
11RELASZ = 8
12RELAENT = 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
27p_start:	.8byte	_start
28p_etext:	.8byte	_etext
29p_bss_start:	.8byte	__bss_start
30p_end:		.8byte	_end
31
32p_toc:		.8byte	.TOC. - p_base
33p_dyn:		.8byte	__dynamic_start - p_base
34p_rela:		.8byte	__rela_dyn_start - p_base
35p_prom:		.8byte	0
36	.weak	_platform_stack_top
37p_pstack:	.8byte	_platform_stack_top
38#else
39p_start:	.long	_start
40p_etext:	.long	_etext
41p_bss_start:	.long	__bss_start
42p_end:		.long	_end
43
44	.weak	_platform_stack_top
45p_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
55p_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
729:	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
7911:	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 */
8712:	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 */
9310:	/* 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
1042:	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 */
1153:	lwz	r9,p_start-p_base(r10)	/* note: these are relocated now */
116	lwz	r8,p_etext-p_base(r10)
1174:	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
1295:	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 */
1416:
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
1629:	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
16910:	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 */
17711:	addi	r11,r11,16
178	b	9b
17912:
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
19413:	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 */
2053:	ld	r9,p_start-p_base(r10)	/* note: these are relocated now */
206	ld	r8,p_etext-p_base(r10)
2074:	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
2195:	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 */
2316:
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
259prom:
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
2780:	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
2871:	/* 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