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