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