xref: /openbmc/linux/arch/powerpc/boot/crt0.S (revision f16e9684)
194b212c2SPaul Mackerras/*
294b212c2SPaul Mackerras * Copyright (C) Paul Mackerras 1997.
394b212c2SPaul Mackerras *
4f16e9684SCédric Le Goater * Adapted for 64 bit LE PowerPC by Andrew Tauferner
5f16e9684SCédric Le Goater *
694b212c2SPaul Mackerras * This program is free software; you can redistribute it and/or
794b212c2SPaul Mackerras * modify it under the terms of the GNU General Public License
894b212c2SPaul Mackerras * as published by the Free Software Foundation; either version
994b212c2SPaul Mackerras * 2 of the License, or (at your option) any later version.
1094b212c2SPaul Mackerras *
1194b212c2SPaul Mackerras */
1294b212c2SPaul Mackerras
1394b212c2SPaul Mackerras#include "ppc_asm.h"
1494b212c2SPaul Mackerras
15f16e9684SCédric Le GoaterRELA = 7
16f16e9684SCédric Le GoaterRELACOUNT = 0x6ffffff9
17f16e9684SCédric Le Goater
1894b212c2SPaul Mackerras	.text
196975a783SMichael Ellerman	/* A procedure descriptor used when booting this as a COFF file.
206975a783SMichael Ellerman	 * When making COFF, this comes first in the link and we're
216975a783SMichael Ellerman	 * linked at 0x500000.
226975a783SMichael Ellerman	 */
23f40e524eSPaul Mackerras	.globl	_zimage_start_opd
2466a45dd3SPaul Mackerras_zimage_start_opd:
256975a783SMichael Ellerman	.long	0x500000, 0, 0, 0
266975a783SMichael Ellerman
27f16e9684SCédric Le Goater#ifdef __powerpc64__
28f16e9684SCédric Le Goater.balign 8
29f16e9684SCédric Le Goaterp_start:	.llong	_start
30f16e9684SCédric Le Goaterp_etext:	.llong	_etext
31f16e9684SCédric Le Goaterp_bss_start:	.llong	__bss_start
32f16e9684SCédric Le Goaterp_end:		.llong	_end
33f16e9684SCédric Le Goater
34f16e9684SCédric Le Goaterp_toc:		.llong	__toc_start + 0x8000 - p_base
35f16e9684SCédric Le Goaterp_dyn:		.llong	__dynamic_start - p_base
36f16e9684SCédric Le Goaterp_rela:		.llong	__rela_dyn_start - p_base
37f16e9684SCédric Le Goaterp_prom:		.llong	0
38f16e9684SCédric Le Goater	.weak	_platform_stack_top
39f16e9684SCédric Le Goaterp_pstack:	.llong	_platform_stack_top
40f16e9684SCédric Le Goater#else
416975a783SMichael Ellermanp_start:	.long	_start
426975a783SMichael Ellermanp_etext:	.long	_etext
436975a783SMichael Ellermanp_bss_start:	.long	__bss_start
446975a783SMichael Ellermanp_end:		.long	_end
456975a783SMichael Ellerman
466975a783SMichael Ellerman	.weak	_platform_stack_top
476975a783SMichael Ellermanp_pstack:	.long	_platform_stack_top
48f16e9684SCédric Le Goater#endif
4966a45dd3SPaul Mackerras
50cd197ffcSDavid Gibson	.weak	_zimage_start
5194b212c2SPaul Mackerras	.globl	_zimage_start
5294b212c2SPaul Mackerras_zimage_start:
53160cc3ecSMilton Miller	.globl	_zimage_start_lib
54160cc3ecSMilton Miller_zimage_start_lib:
5566a45dd3SPaul Mackerras	/* Work out the offset between the address we were linked at
5666a45dd3SPaul Mackerras	   and the address where we're running. */
576975a783SMichael Ellerman	bl	.+4
586975a783SMichael Ellermanp_base:	mflr	r10		/* r10 now points to runtime addr of p_base */
59f16e9684SCédric Le Goater#ifndef __powerpc64__
606975a783SMichael Ellerman	/* grab the link address of the dynamic section in r11 */
616975a783SMichael Ellerman	addis	r11,r10,(_GLOBAL_OFFSET_TABLE_-p_base)@ha
626975a783SMichael Ellerman	lwz	r11,(_GLOBAL_OFFSET_TABLE_-p_base)@l(r11)
636975a783SMichael Ellerman	cmpwi	r11,0
646975a783SMichael Ellerman	beq	3f		/* if not linked -pie */
656975a783SMichael Ellerman	/* get the runtime address of the dynamic section in r12 */
666975a783SMichael Ellerman	.weak	__dynamic_start
676975a783SMichael Ellerman	addis	r12,r10,(__dynamic_start-p_base)@ha
686975a783SMichael Ellerman	addi	r12,r12,(__dynamic_start-p_base)@l
696975a783SMichael Ellerman	subf	r11,r11,r12	/* runtime - linktime offset */
7094b212c2SPaul Mackerras
716975a783SMichael Ellerman	/* The dynamic section contains a series of tagged entries.
726975a783SMichael Ellerman	 * We need the RELA and RELACOUNT entries. */
736975a783SMichael Ellerman	li	r9,0
746975a783SMichael Ellerman	li	r0,0
756975a783SMichael Ellerman9:	lwz	r8,0(r12)	/* get tag */
766975a783SMichael Ellerman	cmpwi	r8,0
776975a783SMichael Ellerman	beq	10f		/* end of list */
786975a783SMichael Ellerman	cmpwi	r8,RELA
796975a783SMichael Ellerman	bne	11f
806975a783SMichael Ellerman	lwz	r9,4(r12)	/* get RELA pointer in r9 */
816975a783SMichael Ellerman	b	12f
826975a783SMichael Ellerman11:	addis	r8,r8,(-RELACOUNT)@ha
836975a783SMichael Ellerman	cmpwi	r8,RELACOUNT@l
846975a783SMichael Ellerman	bne	12f
856975a783SMichael Ellerman	lwz	r0,4(r12)	/* get RELACOUNT value in r0 */
866975a783SMichael Ellerman12:	addi	r12,r12,8
876975a783SMichael Ellerman	b	9b
886975a783SMichael Ellerman
896975a783SMichael Ellerman	/* The relocation section contains a list of relocations.
906975a783SMichael Ellerman	 * We now do the R_PPC_RELATIVE ones, which point to words
916975a783SMichael Ellerman	 * which need to be initialized with addend + offset.
926975a783SMichael Ellerman	 * The R_PPC_RELATIVE ones come first and there are RELACOUNT
936975a783SMichael Ellerman	 * of them. */
946975a783SMichael Ellerman10:	/* skip relocation if we don't have both */
956975a783SMichael Ellerman	cmpwi	r0,0
9668643cfbSOlaf Hering	beq	3f
976975a783SMichael Ellerman	cmpwi	r9,0
986975a783SMichael Ellerman	beq	3f
996975a783SMichael Ellerman
1006975a783SMichael Ellerman	add	r9,r9,r11	/* Relocate RELA pointer */
1016975a783SMichael Ellerman	mtctr	r0
1026975a783SMichael Ellerman2:	lbz	r0,4+3(r9)	/* ELF32_R_INFO(reloc->r_info) */
1036975a783SMichael Ellerman	cmpwi	r0,22		/* R_PPC_RELATIVE */
1046975a783SMichael Ellerman	bne	3f
1056975a783SMichael Ellerman	lwz	r12,0(r9)	/* reloc->r_offset */
1066975a783SMichael Ellerman	lwz	r0,8(r9)	/* reloc->r_addend */
1076975a783SMichael Ellerman	add	r0,r0,r11
1086975a783SMichael Ellerman	stwx	r0,r11,r12
1096975a783SMichael Ellerman	addi	r9,r9,12
11068643cfbSOlaf Hering	bdnz	2b
11194b212c2SPaul Mackerras
112cd197ffcSDavid Gibson	/* Do a cache flush for our text, in case the loader didn't */
1136975a783SMichael Ellerman3:	lwz	r9,p_start-p_base(r10)	/* note: these are relocated now */
1146975a783SMichael Ellerman	lwz	r8,p_etext-p_base(r10)
11568643cfbSOlaf Hering4:	dcbf	r0,r9
11694b212c2SPaul Mackerras	icbi	r0,r9
11794b212c2SPaul Mackerras	addi	r9,r9,0x20
118eacb1962SOlaf Hering	cmplw	cr0,r9,r8
11968643cfbSOlaf Hering	blt	4b
12094b212c2SPaul Mackerras	sync
12194b212c2SPaul Mackerras	isync
12294b212c2SPaul Mackerras
123cd197ffcSDavid Gibson	/* Clear the BSS */
1246975a783SMichael Ellerman	lwz	r9,p_bss_start-p_base(r10)
1256975a783SMichael Ellerman	lwz	r8,p_end-p_base(r10)
1266975a783SMichael Ellerman	li	r0,0
1276975a783SMichael Ellerman5:	stw	r0,0(r9)
128cd197ffcSDavid Gibson	addi	r9,r9,4
129cd197ffcSDavid Gibson	cmplw	cr0,r9,r8
130cd197ffcSDavid Gibson	blt	5b
13194b212c2SPaul Mackerras
132cd197ffcSDavid Gibson	/* Possibly set up a custom stack */
1336975a783SMichael Ellerman	lwz	r8,p_pstack-p_base(r10)
134cd197ffcSDavid Gibson	cmpwi	r8,0
135cd197ffcSDavid Gibson	beq	6f
136cd197ffcSDavid Gibson	lwz	r1,0(r8)
137cd197ffcSDavid Gibson	li	r0,0
138cd197ffcSDavid Gibson	stwu	r0,-16(r1)	/* establish a stack frame */
139cd197ffcSDavid Gibson6:
140f16e9684SCédric Le Goater#else /* __powerpc64__ */
141f16e9684SCédric Le Goater	/* Save the prom pointer at p_prom. */
142f16e9684SCédric Le Goater	std	r5,(p_prom-p_base)(r10)
143cd197ffcSDavid Gibson
144f16e9684SCédric Le Goater	/* Set r2 to the TOC. */
145f16e9684SCédric Le Goater	ld	r2,(p_toc-p_base)(r10)
146f16e9684SCédric Le Goater	add	r2,r2,r10
147f16e9684SCédric Le Goater
148f16e9684SCédric Le Goater	/* Grab the link address of the dynamic section in r11. */
149f16e9684SCédric Le Goater	ld	r11,-32768(r2)
150f16e9684SCédric Le Goater	cmpwi	r11,0
151f16e9684SCédric Le Goater	beq	3f              /* if not linked -pie then no dynamic section */
152f16e9684SCédric Le Goater
153f16e9684SCédric Le Goater	ld	r11,(p_dyn-p_base)(r10)
154f16e9684SCédric Le Goater	add	r11,r11,r10
155f16e9684SCédric Le Goater	ld	r9,(p_rela-p_base)(r10)
156f16e9684SCédric Le Goater	add	r9,r9,r10
157f16e9684SCédric Le Goater
158f16e9684SCédric Le Goater	li	r7,0
159f16e9684SCédric Le Goater	li	r8,0
160f16e9684SCédric Le Goater9:	ld	r6,0(r11)       /* get tag */
161f16e9684SCédric Le Goater	cmpdi	r6,0
162f16e9684SCédric Le Goater	beq	12f              /* end of list */
163f16e9684SCédric Le Goater	cmpdi	r6,RELA
164f16e9684SCédric Le Goater	bne	10f
165f16e9684SCédric Le Goater	ld	r7,8(r11)       /* get RELA pointer in r7 */
166f16e9684SCédric Le Goater	b	11f
167f16e9684SCédric Le Goater10:	addis	r6,r6,(-RELACOUNT)@ha
168f16e9684SCédric Le Goater	cmpdi	r6,RELACOUNT@l
169f16e9684SCédric Le Goater	bne	11f
170f16e9684SCédric Le Goater	ld	r8,8(r11)       /* get RELACOUNT value in r8 */
171f16e9684SCédric Le Goater11:	addi	r11,r11,16
172f16e9684SCédric Le Goater	b	9b
173f16e9684SCédric Le Goater12:
174f16e9684SCédric Le Goater	cmpdi	r7,0            /* check we have both RELA and RELACOUNT */
175f16e9684SCédric Le Goater	cmpdi	cr1,r8,0
176f16e9684SCédric Le Goater	beq	3f
177f16e9684SCédric Le Goater	beq	cr1,3f
178f16e9684SCédric Le Goater
179f16e9684SCédric Le Goater	/* Calcuate the runtime offset. */
180f16e9684SCédric Le Goater	subf	r7,r7,r9
181f16e9684SCédric Le Goater
182f16e9684SCédric Le Goater	/* Run through the list of relocations and process the
183f16e9684SCédric Le Goater	 * R_PPC64_RELATIVE ones. */
184f16e9684SCédric Le Goater	mtctr	r8
185f16e9684SCédric Le Goater13:	ld	r0,8(r9)        /* ELF64_R_TYPE(reloc->r_info) */
186f16e9684SCédric Le Goater	cmpdi	r0,22           /* R_PPC64_RELATIVE */
187f16e9684SCédric Le Goater	bne	3f
188f16e9684SCédric Le Goater	ld	r6,0(r9)        /* reloc->r_offset */
189f16e9684SCédric Le Goater	ld	r0,16(r9)       /* reloc->r_addend */
190f16e9684SCédric Le Goater	add	r0,r0,r7
191f16e9684SCédric Le Goater	stdx	r0,r7,r6
192f16e9684SCédric Le Goater	addi	r9,r9,24
193f16e9684SCédric Le Goater	bdnz	13b
194f16e9684SCédric Le Goater
195f16e9684SCédric Le Goater	/* Do a cache flush for our text, in case the loader didn't */
196f16e9684SCédric Le Goater3:	ld	r9,p_start-p_base(r10)	/* note: these are relocated now */
197f16e9684SCédric Le Goater	ld	r8,p_etext-p_base(r10)
198f16e9684SCédric Le Goater4:	dcbf	r0,r9
199f16e9684SCédric Le Goater	icbi	r0,r9
200f16e9684SCédric Le Goater	addi	r9,r9,0x20
201f16e9684SCédric Le Goater	cmpld	cr0,r9,r8
202f16e9684SCédric Le Goater	blt	4b
203f16e9684SCédric Le Goater	sync
204f16e9684SCédric Le Goater	isync
205f16e9684SCédric Le Goater
206f16e9684SCédric Le Goater	/* Clear the BSS */
207f16e9684SCédric Le Goater	ld	r9,p_bss_start-p_base(r10)
208f16e9684SCédric Le Goater	ld	r8,p_end-p_base(r10)
209f16e9684SCédric Le Goater	li	r0,0
210f16e9684SCédric Le Goater5:	std	r0,0(r9)
211f16e9684SCédric Le Goater	addi	r9,r9,8
212f16e9684SCédric Le Goater	cmpld	cr0,r9,r8
213f16e9684SCédric Le Goater	blt	5b
214f16e9684SCédric Le Goater
215f16e9684SCédric Le Goater	/* Possibly set up a custom stack */
216f16e9684SCédric Le Goater	ld	r8,p_pstack-p_base(r10)
217f16e9684SCédric Le Goater	cmpdi	r8,0
218f16e9684SCédric Le Goater	beq	6f
219f16e9684SCédric Le Goater	ld	r1,0(r8)
220f16e9684SCédric Le Goater	li	r0,0
221f16e9684SCédric Le Goater	stdu	r0,-16(r1)	/* establish a stack frame */
222f16e9684SCédric Le Goater6:
223f16e9684SCédric Le Goater#endif  /* __powerpc64__ */
224cd197ffcSDavid Gibson	/* Call platform_init() */
225cd197ffcSDavid Gibson	bl	platform_init
226cd197ffcSDavid Gibson
227cd197ffcSDavid Gibson	/* Call start */
228cd197ffcSDavid Gibson	b	start
22993d39210SCédric Le Goater
23093d39210SCédric Le Goater#ifdef __powerpc64__
23193d39210SCédric Le Goater
23293d39210SCédric Le Goater#define PROM_FRAME_SIZE 512
23393d39210SCédric Le Goater#define SAVE_GPR(n, base)       std     n,8*(n)(base)
23493d39210SCédric Le Goater#define REST_GPR(n, base)       ld      n,8*(n)(base)
23593d39210SCédric Le Goater#define SAVE_2GPRS(n, base)     SAVE_GPR(n, base); SAVE_GPR(n+1, base)
23693d39210SCédric Le Goater#define SAVE_4GPRS(n, base)     SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base)
23793d39210SCédric Le Goater#define SAVE_8GPRS(n, base)     SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base)
23893d39210SCédric Le Goater#define SAVE_10GPRS(n, base)    SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base)
23993d39210SCédric Le Goater#define REST_2GPRS(n, base)     REST_GPR(n, base); REST_GPR(n+1, base)
24093d39210SCédric Le Goater#define REST_4GPRS(n, base)     REST_2GPRS(n, base); REST_2GPRS(n+2, base)
24193d39210SCédric Le Goater#define REST_8GPRS(n, base)     REST_4GPRS(n, base); REST_4GPRS(n+4, base)
24293d39210SCédric Le Goater#define REST_10GPRS(n, base)    REST_8GPRS(n, base); REST_2GPRS(n+8, base)
24393d39210SCédric Le Goater
24493d39210SCédric Le Goater/* prom handles the jump into and return from firmware.  The prom args pointer
24593d39210SCédric Le Goater   is loaded in r3. */
24693d39210SCédric Le Goater.globl prom
24793d39210SCédric Le Goaterprom:
24893d39210SCédric Le Goater	mflr	r0
24993d39210SCédric Le Goater	std	r0,16(r1)
25093d39210SCédric Le Goater	stdu	r1,-PROM_FRAME_SIZE(r1) /* Save SP and create stack space */
25193d39210SCédric Le Goater
25293d39210SCédric Le Goater	SAVE_GPR(2, r1)
25393d39210SCédric Le Goater	SAVE_GPR(13, r1)
25493d39210SCédric Le Goater	SAVE_8GPRS(14, r1)
25593d39210SCédric Le Goater	SAVE_10GPRS(22, r1)
25693d39210SCédric Le Goater	mfcr    r10
25793d39210SCédric Le Goater	std     r10,8*32(r1)
25893d39210SCédric Le Goater	mfmsr   r10
25993d39210SCédric Le Goater	std     r10,8*33(r1)
26093d39210SCédric Le Goater
26193d39210SCédric Le Goater	/* remove MSR_LE from msr but keep MSR_SF */
26293d39210SCédric Le Goater	mfmsr	r10
26393d39210SCédric Le Goater	rldicr	r10,r10,0,62
26493d39210SCédric Le Goater	mtsrr1	r10
26593d39210SCédric Le Goater
26693d39210SCédric Le Goater	/* Load FW address, set LR to label 1, and jump to FW */
26793d39210SCédric Le Goater	bl	0f
26893d39210SCédric Le Goater0:	mflr	r10
26993d39210SCédric Le Goater	addi	r11,r10,(1f-0b)
27093d39210SCédric Le Goater	mtlr	r11
27193d39210SCédric Le Goater
27293d39210SCédric Le Goater	ld	r10,(p_prom-0b)(r10)
27393d39210SCédric Le Goater	mtsrr0	r10
27493d39210SCédric Le Goater
27593d39210SCédric Le Goater	rfid
27693d39210SCédric Le Goater
27793d39210SCédric Le Goater1:	/* Return from OF */
27893d39210SCédric Le Goater
27993d39210SCédric Le Goater	/* Restore registers and return. */
28093d39210SCédric Le Goater	rldicl  r1,r1,0,32
28193d39210SCédric Le Goater
28293d39210SCédric Le Goater	/* Restore the MSR (back to 64 bits) */
28393d39210SCédric Le Goater	ld      r10,8*(33)(r1)
28493d39210SCédric Le Goater	mtmsr	r10
28593d39210SCédric Le Goater	isync
28693d39210SCédric Le Goater
28793d39210SCédric Le Goater	/* Restore other registers */
28893d39210SCédric Le Goater	REST_GPR(2, r1)
28993d39210SCédric Le Goater	REST_GPR(13, r1)
29093d39210SCédric Le Goater	REST_8GPRS(14, r1)
29193d39210SCédric Le Goater	REST_10GPRS(22, r1)
29293d39210SCédric Le Goater	ld      r10,8*32(r1)
29393d39210SCédric Le Goater	mtcr	r10
29493d39210SCédric Le Goater
29593d39210SCédric Le Goater	addi    r1,r1,PROM_FRAME_SIZE
29693d39210SCédric Le Goater	ld      r0,16(r1)
29793d39210SCédric Le Goater	mtlr    r0
29893d39210SCédric Le Goater	blr
29993d39210SCédric Le Goater#endif
300