162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) Paul Mackerras 1997.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Adapted for 64 bit LE PowerPC by Andrew Tauferner
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "ppc_asm.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ciRELA = 7
1162306a36Sopenharmony_ciRELASZ = 8
1262306a36Sopenharmony_ciRELAENT = 9
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci	.data
1562306a36Sopenharmony_ci	/* A procedure descriptor used when booting this as a COFF file.
1662306a36Sopenharmony_ci	 * When making COFF, this comes first in the link and we're
1762306a36Sopenharmony_ci	 * linked at 0x500000.
1862306a36Sopenharmony_ci	 */
1962306a36Sopenharmony_ci	.globl	_zimage_start_opd
2062306a36Sopenharmony_ci_zimage_start_opd:
2162306a36Sopenharmony_ci	.long	0x500000, 0, 0, 0
2262306a36Sopenharmony_ci	.text
2362306a36Sopenharmony_ci	b	_zimage_start
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#ifdef __powerpc64__
2662306a36Sopenharmony_ci.balign 8
2762306a36Sopenharmony_cip_start:	.8byte	_start
2862306a36Sopenharmony_cip_etext:	.8byte	_etext
2962306a36Sopenharmony_cip_bss_start:	.8byte	__bss_start
3062306a36Sopenharmony_cip_end:		.8byte	_end
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cip_toc:		.8byte	.TOC. - p_base
3362306a36Sopenharmony_cip_dyn:		.8byte	__dynamic_start - p_base
3462306a36Sopenharmony_cip_rela:		.8byte	__rela_dyn_start - p_base
3562306a36Sopenharmony_cip_prom:		.8byte	0
3662306a36Sopenharmony_ci	.weak	_platform_stack_top
3762306a36Sopenharmony_cip_pstack:	.8byte	_platform_stack_top
3862306a36Sopenharmony_ci#else
3962306a36Sopenharmony_cip_start:	.long	_start
4062306a36Sopenharmony_cip_etext:	.long	_etext
4162306a36Sopenharmony_cip_bss_start:	.long	__bss_start
4262306a36Sopenharmony_cip_end:		.long	_end
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	.weak	_platform_stack_top
4562306a36Sopenharmony_cip_pstack:	.long	_platform_stack_top
4662306a36Sopenharmony_ci#endif
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	.weak	_zimage_start
4962306a36Sopenharmony_ci_zimage_start:
5062306a36Sopenharmony_ci	.globl	_zimage_start_lib
5162306a36Sopenharmony_ci_zimage_start_lib:
5262306a36Sopenharmony_ci	/* Work out the offset between the address we were linked at
5362306a36Sopenharmony_ci	   and the address where we're running. */
5462306a36Sopenharmony_ci	bcl	20,31,.+4
5562306a36Sopenharmony_cip_base:	mflr	r10		/* r10 now points to runtime addr of p_base */
5662306a36Sopenharmony_ci#ifndef __powerpc64__
5762306a36Sopenharmony_ci	/* grab the link address of the dynamic section in r11 */
5862306a36Sopenharmony_ci	addis	r11,r10,(_GLOBAL_OFFSET_TABLE_-p_base)@ha
5962306a36Sopenharmony_ci	lwz	r11,(_GLOBAL_OFFSET_TABLE_-p_base)@l(r11)
6062306a36Sopenharmony_ci	cmpwi	r11,0
6162306a36Sopenharmony_ci	beq	3f		/* if not linked -pie */
6262306a36Sopenharmony_ci	/* get the runtime address of the dynamic section in r12 */
6362306a36Sopenharmony_ci	.weak	__dynamic_start
6462306a36Sopenharmony_ci	addis	r12,r10,(__dynamic_start-p_base)@ha
6562306a36Sopenharmony_ci	addi	r12,r12,(__dynamic_start-p_base)@l
6662306a36Sopenharmony_ci	subf	r11,r11,r12	/* runtime - linktime offset */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* The dynamic section contains a series of tagged entries.
6962306a36Sopenharmony_ci	 * We need the RELA and RELACOUNT entries. */
7062306a36Sopenharmony_ci	li	r9,0
7162306a36Sopenharmony_ci	li	r0,0
7262306a36Sopenharmony_ci9:	lwz	r8,0(r12)	/* get tag */
7362306a36Sopenharmony_ci	cmpwi	r8,0
7462306a36Sopenharmony_ci	beq	10f		/* end of list */
7562306a36Sopenharmony_ci	cmpwi	r8,RELA
7662306a36Sopenharmony_ci	bne	11f
7762306a36Sopenharmony_ci	lwz	r9,4(r12)	/* get RELA pointer in r9 */
7862306a36Sopenharmony_ci	b	12f
7962306a36Sopenharmony_ci11:	cmpwi	r8,RELASZ
8062306a36Sopenharmony_ci	bne	.Lcheck_for_relaent
8162306a36Sopenharmony_ci	lwz	r0,4(r12)       /* get RELASZ value in r0 */
8262306a36Sopenharmony_ci	b	12f
8362306a36Sopenharmony_ci.Lcheck_for_relaent:
8462306a36Sopenharmony_ci	cmpwi	r8,RELAENT
8562306a36Sopenharmony_ci	bne	12f
8662306a36Sopenharmony_ci	lwz     r14,4(r12)      /* get RELAENT value in r14 */
8762306a36Sopenharmony_ci12:	addi	r12,r12,8
8862306a36Sopenharmony_ci	b	9b
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* The relocation section contains a list of relocations.
9162306a36Sopenharmony_ci	 * We now do the R_PPC_RELATIVE ones, which point to words
9262306a36Sopenharmony_ci	 * which need to be initialized with addend + offset */
9362306a36Sopenharmony_ci10:	/* skip relocation if we don't have both */
9462306a36Sopenharmony_ci	cmpwi	r0,0
9562306a36Sopenharmony_ci	beq	3f
9662306a36Sopenharmony_ci	cmpwi	r9,0
9762306a36Sopenharmony_ci	beq	3f
9862306a36Sopenharmony_ci	cmpwi	r14,0
9962306a36Sopenharmony_ci	beq	3f
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	add	r9,r9,r11	/* Relocate RELA pointer */
10262306a36Sopenharmony_ci	divwu   r0,r0,r14       /* RELASZ / RELAENT */
10362306a36Sopenharmony_ci	mtctr	r0
10462306a36Sopenharmony_ci2:	lbz	r0,4+3(r9)	/* ELF32_R_INFO(reloc->r_info) */
10562306a36Sopenharmony_ci	cmpwi	r0,22		/* R_PPC_RELATIVE */
10662306a36Sopenharmony_ci	bne	.Lnext
10762306a36Sopenharmony_ci	lwz	r12,0(r9)	/* reloc->r_offset */
10862306a36Sopenharmony_ci	lwz	r0,8(r9)	/* reloc->r_addend */
10962306a36Sopenharmony_ci	add	r0,r0,r11
11062306a36Sopenharmony_ci	stwx	r0,r11,r12
11162306a36Sopenharmony_ci.Lnext:	add	r9,r9,r14
11262306a36Sopenharmony_ci	bdnz	2b
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* Do a cache flush for our text, in case the loader didn't */
11562306a36Sopenharmony_ci3:	lwz	r9,p_start-p_base(r10)	/* note: these are relocated now */
11662306a36Sopenharmony_ci	lwz	r8,p_etext-p_base(r10)
11762306a36Sopenharmony_ci4:	dcbf	r0,r9
11862306a36Sopenharmony_ci	icbi	r0,r9
11962306a36Sopenharmony_ci	addi	r9,r9,0x20
12062306a36Sopenharmony_ci	cmplw	cr0,r9,r8
12162306a36Sopenharmony_ci	blt	4b
12262306a36Sopenharmony_ci	sync
12362306a36Sopenharmony_ci	isync
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Clear the BSS */
12662306a36Sopenharmony_ci	lwz	r9,p_bss_start-p_base(r10)
12762306a36Sopenharmony_ci	lwz	r8,p_end-p_base(r10)
12862306a36Sopenharmony_ci	li	r0,0
12962306a36Sopenharmony_ci5:	stw	r0,0(r9)
13062306a36Sopenharmony_ci	addi	r9,r9,4
13162306a36Sopenharmony_ci	cmplw	cr0,r9,r8
13262306a36Sopenharmony_ci	blt	5b
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* Possibly set up a custom stack */
13562306a36Sopenharmony_ci	lwz	r8,p_pstack-p_base(r10)
13662306a36Sopenharmony_ci	cmpwi	r8,0
13762306a36Sopenharmony_ci	beq	6f
13862306a36Sopenharmony_ci	lwz	r1,0(r8)
13962306a36Sopenharmony_ci	li	r0,0
14062306a36Sopenharmony_ci	stwu	r0,-16(r1)	/* establish a stack frame */
14162306a36Sopenharmony_ci6:
14262306a36Sopenharmony_ci#else /* __powerpc64__ */
14362306a36Sopenharmony_ci	/* Save the prom pointer at p_prom. */
14462306a36Sopenharmony_ci	std	r5,(p_prom-p_base)(r10)
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* Set r2 to the TOC. */
14762306a36Sopenharmony_ci	ld	r2,(p_toc-p_base)(r10)
14862306a36Sopenharmony_ci	add	r2,r2,r10
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Grab the link address of the dynamic section in r11. */
15162306a36Sopenharmony_ci	ld	r11,-32768(r2)
15262306a36Sopenharmony_ci	cmpwi	r11,0
15362306a36Sopenharmony_ci	beq	3f              /* if not linked -pie then no dynamic section */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ld	r11,(p_dyn-p_base)(r10)
15662306a36Sopenharmony_ci	add	r11,r11,r10
15762306a36Sopenharmony_ci	ld	r9,(p_rela-p_base)(r10)
15862306a36Sopenharmony_ci	add	r9,r9,r10
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	li	r13,0
16162306a36Sopenharmony_ci	li	r8,0
16262306a36Sopenharmony_ci9:	ld	r12,0(r11)       /* get tag */
16362306a36Sopenharmony_ci	cmpdi	r12,0
16462306a36Sopenharmony_ci	beq	12f              /* end of list */
16562306a36Sopenharmony_ci	cmpdi	r12,RELA
16662306a36Sopenharmony_ci	bne	10f
16762306a36Sopenharmony_ci	ld	r13,8(r11)       /* get RELA pointer in r13 */
16862306a36Sopenharmony_ci	b	11f
16962306a36Sopenharmony_ci10:	cmpwi   r12,RELASZ
17062306a36Sopenharmony_ci	bne	.Lcheck_for_relaent
17162306a36Sopenharmony_ci	lwz	r8,8(r11)	/* get RELASZ pointer in r8 */
17262306a36Sopenharmony_ci	b	11f
17362306a36Sopenharmony_ci.Lcheck_for_relaent:
17462306a36Sopenharmony_ci	cmpwi	r12,RELAENT
17562306a36Sopenharmony_ci	bne     11f
17662306a36Sopenharmony_ci	lwz     r14,8(r11)      /* get RELAENT pointer in r14 */
17762306a36Sopenharmony_ci11:	addi	r11,r11,16
17862306a36Sopenharmony_ci	b	9b
17962306a36Sopenharmony_ci12:
18062306a36Sopenharmony_ci	cmpdi	r13,0            /* check we have both RELA, RELASZ, RELAENT*/
18162306a36Sopenharmony_ci	cmpdi	cr1,r8,0
18262306a36Sopenharmony_ci	beq	3f
18362306a36Sopenharmony_ci	beq	cr1,3f
18462306a36Sopenharmony_ci	cmpdi	r14,0
18562306a36Sopenharmony_ci	beq	3f
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* Calcuate the runtime offset. */
18862306a36Sopenharmony_ci	subf	r13,r13,r9
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* Run through the list of relocations and process the
19162306a36Sopenharmony_ci	 * R_PPC64_RELATIVE ones. */
19262306a36Sopenharmony_ci	divdu   r8,r8,r14       /* RELASZ / RELAENT */
19362306a36Sopenharmony_ci	mtctr	r8
19462306a36Sopenharmony_ci13:	ld	r0,8(r9)        /* ELF64_R_TYPE(reloc->r_info) */
19562306a36Sopenharmony_ci	cmpdi	r0,22           /* R_PPC64_RELATIVE */
19662306a36Sopenharmony_ci	bne	.Lnext
19762306a36Sopenharmony_ci	ld	r12,0(r9)        /* reloc->r_offset */
19862306a36Sopenharmony_ci	ld	r0,16(r9)       /* reloc->r_addend */
19962306a36Sopenharmony_ci	add	r0,r0,r13
20062306a36Sopenharmony_ci	stdx	r0,r13,r12
20162306a36Sopenharmony_ci.Lnext:	add	r9,r9,r14
20262306a36Sopenharmony_ci	bdnz	13b
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* Do a cache flush for our text, in case the loader didn't */
20562306a36Sopenharmony_ci3:	ld	r9,p_start-p_base(r10)	/* note: these are relocated now */
20662306a36Sopenharmony_ci	ld	r8,p_etext-p_base(r10)
20762306a36Sopenharmony_ci4:	dcbf	r0,r9
20862306a36Sopenharmony_ci	icbi	r0,r9
20962306a36Sopenharmony_ci	addi	r9,r9,0x20
21062306a36Sopenharmony_ci	cmpld	cr0,r9,r8
21162306a36Sopenharmony_ci	blt	4b
21262306a36Sopenharmony_ci	sync
21362306a36Sopenharmony_ci	isync
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Clear the BSS */
21662306a36Sopenharmony_ci	ld	r9,p_bss_start-p_base(r10)
21762306a36Sopenharmony_ci	ld	r8,p_end-p_base(r10)
21862306a36Sopenharmony_ci	li	r0,0
21962306a36Sopenharmony_ci5:	std	r0,0(r9)
22062306a36Sopenharmony_ci	addi	r9,r9,8
22162306a36Sopenharmony_ci	cmpld	cr0,r9,r8
22262306a36Sopenharmony_ci	blt	5b
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* Possibly set up a custom stack */
22562306a36Sopenharmony_ci	ld	r8,p_pstack-p_base(r10)
22662306a36Sopenharmony_ci	cmpdi	r8,0
22762306a36Sopenharmony_ci	beq	6f
22862306a36Sopenharmony_ci	ld	r1,0(r8)
22962306a36Sopenharmony_ci	li	r0,0
23062306a36Sopenharmony_ci	stdu	r0,-112(r1)	/* establish a stack frame */
23162306a36Sopenharmony_ci6:
23262306a36Sopenharmony_ci#endif  /* __powerpc64__ */
23362306a36Sopenharmony_ci	/* Call platform_init() */
23462306a36Sopenharmony_ci	bl	platform_init
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* Call start */
23762306a36Sopenharmony_ci	b	start
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci#ifdef __powerpc64__
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci#define PROM_FRAME_SIZE 512
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci.macro OP_REGS op, width, start, end, base, offset
24462306a36Sopenharmony_ci	.Lreg=\start
24562306a36Sopenharmony_ci	.rept (\end - \start + 1)
24662306a36Sopenharmony_ci	\op	.Lreg,\offset+\width*.Lreg(\base)
24762306a36Sopenharmony_ci	.Lreg=.Lreg+1
24862306a36Sopenharmony_ci	.endr
24962306a36Sopenharmony_ci.endm
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci#define SAVE_GPRS(start, end, base)	OP_REGS std, 8, start, end, base, 0
25262306a36Sopenharmony_ci#define REST_GPRS(start, end, base)	OP_REGS ld, 8, start, end, base, 0
25362306a36Sopenharmony_ci#define SAVE_GPR(n, base)		SAVE_GPRS(n, n, base)
25462306a36Sopenharmony_ci#define REST_GPR(n, base)		REST_GPRS(n, n, base)
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/* prom handles the jump into and return from firmware.  The prom args pointer
25762306a36Sopenharmony_ci   is loaded in r3. */
25862306a36Sopenharmony_ci.globl prom
25962306a36Sopenharmony_ciprom:
26062306a36Sopenharmony_ci	mflr	r0
26162306a36Sopenharmony_ci	std	r0,16(r1)
26262306a36Sopenharmony_ci	stdu	r1,-PROM_FRAME_SIZE(r1) /* Save SP and create stack space */
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	SAVE_GPR(2, r1)
26562306a36Sopenharmony_ci	SAVE_GPRS(13, 31, r1)
26662306a36Sopenharmony_ci	mfcr    r10
26762306a36Sopenharmony_ci	std     r10,8*32(r1)
26862306a36Sopenharmony_ci	mfmsr   r10
26962306a36Sopenharmony_ci	std     r10,8*33(r1)
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* remove MSR_LE from msr but keep MSR_SF */
27262306a36Sopenharmony_ci	mfmsr	r10
27362306a36Sopenharmony_ci	rldicr	r10,r10,0,62
27462306a36Sopenharmony_ci	mtsrr1	r10
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* Load FW address, set LR to label 1, and jump to FW */
27762306a36Sopenharmony_ci	bcl	20,31,0f
27862306a36Sopenharmony_ci0:	mflr	r10
27962306a36Sopenharmony_ci	addi	r11,r10,(1f-0b)
28062306a36Sopenharmony_ci	mtlr	r11
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	ld	r10,(p_prom-0b)(r10)
28362306a36Sopenharmony_ci	mtsrr0	r10
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	rfid
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci1:	/* Return from OF */
28862306a36Sopenharmony_ci	FIXUP_ENDIAN
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Restore registers and return. */
29162306a36Sopenharmony_ci	rldicl  r1,r1,0,32
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Restore the MSR (back to 64 bits) */
29462306a36Sopenharmony_ci	ld      r10,8*(33)(r1)
29562306a36Sopenharmony_ci	mtmsr	r10
29662306a36Sopenharmony_ci	isync
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* Restore other registers */
29962306a36Sopenharmony_ci	REST_GPR(2, r1)
30062306a36Sopenharmony_ci	REST_GPRS(13, 31, r1)
30162306a36Sopenharmony_ci	ld      r10,8*32(r1)
30262306a36Sopenharmony_ci	mtcr	r10
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	addi    r1,r1,PROM_FRAME_SIZE
30562306a36Sopenharmony_ci	ld      r0,16(r1)
30662306a36Sopenharmony_ci	mtlr    r0
30762306a36Sopenharmony_ci	blr
30862306a36Sopenharmony_ci#endif
309