| /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
| /* | |
| * Copyright (C) Paul Mackerras 1997. | |
| * | |
| * Adapted for 64 bit LE PowerPC by Andrew Tauferner | |
| */ | |
| #include "ppc_asm.h" | |
| RELA = 7 | |
| RELASZ = 8 | |
| RELAENT = 9 | |
| .data | |
| /* A procedure descriptor used when booting this as a COFF file. | |
| * When making COFF, this comes first in the link and we're | |
| * linked at 0x500000. | |
| */ | |
| .globl _zimage_start_opd | |
| _zimage_start_opd: | |
| .long 0x500000, 0, 0, 0 | |
| .text | |
| b _zimage_start | |
| #ifdef __powerpc64__ | |
| .balign 8 | |
| p_start: .8byte _start | |
| p_etext: .8byte _etext | |
| p_bss_start: .8byte __bss_start | |
| p_end: .8byte _end | |
| p_toc: .8byte .TOC. - p_base | |
| p_dyn: .8byte __dynamic_start - p_base | |
| p_rela: .8byte __rela_dyn_start - p_base | |
| p_prom: .8byte 0 | |
| .weak _platform_stack_top | |
| p_pstack: .8byte _platform_stack_top | |
| #else | |
| p_start: .long _start | |
| p_etext: .long _etext | |
| p_bss_start: .long __bss_start | |
| p_end: .long _end | |
| .weak _platform_stack_top | |
| p_pstack: .long _platform_stack_top | |
| #endif | |
| .weak _zimage_start | |
| _zimage_start: | |
| .globl _zimage_start_lib | |
| _zimage_start_lib: | |
| /* Work out the offset between the address we were linked at | |
| and the address where we're running. */ | |
| bcl 20,31,.+4 | |
| p_base: mflr r10 /* r10 now points to runtime addr of p_base */ | |
| #ifndef __powerpc64__ | |
| /* grab the link address of the dynamic section in r11 */ | |
| addis r11,r10,(_GLOBAL_OFFSET_TABLE_-p_base)@ha | |
| lwz r11,(_GLOBAL_OFFSET_TABLE_-p_base)@l(r11) | |
| cmpwi r11,0 | |
| beq 3f /* if not linked -pie */ | |
| /* get the runtime address of the dynamic section in r12 */ | |
| .weak __dynamic_start | |
| addis r12,r10,(__dynamic_start-p_base)@ha | |
| addi r12,r12,(__dynamic_start-p_base)@l | |
| subf r11,r11,r12 /* runtime - linktime offset */ | |
| /* The dynamic section contains a series of tagged entries. | |
| * We need the RELA and RELACOUNT entries. */ | |
| li r9,0 | |
| li r0,0 | |
| 9: lwz r8,0(r12) /* get tag */ | |
| cmpwi r8,0 | |
| beq 10f /* end of list */ | |
| cmpwi r8,RELA | |
| bne 11f | |
| lwz r9,4(r12) /* get RELA pointer in r9 */ | |
| b 12f | |
| 11: cmpwi r8,RELASZ | |
| bne .Lcheck_for_relaent | |
| lwz r0,4(r12) /* get RELASZ value in r0 */ | |
| b 12f | |
| .Lcheck_for_relaent: | |
| cmpwi r8,RELAENT | |
| bne 12f | |
| lwz r14,4(r12) /* get RELAENT value in r14 */ | |
| 12: addi r12,r12,8 | |
| b 9b | |
| /* The relocation section contains a list of relocations. | |
| * We now do the R_PPC_RELATIVE ones, which point to words | |
| * which need to be initialized with addend + offset */ | |
| 10: /* skip relocation if we don't have both */ | |
| cmpwi r0,0 | |
| beq 3f | |
| cmpwi r9,0 | |
| beq 3f | |
| cmpwi r14,0 | |
| beq 3f | |
| add r9,r9,r11 /* Relocate RELA pointer */ | |
| divwu r0,r0,r14 /* RELASZ / RELAENT */ | |
| mtctr r0 | |
| 2: lbz r0,4+3(r9) /* ELF32_R_INFO(reloc->r_info) */ | |
| cmpwi r0,22 /* R_PPC_RELATIVE */ | |
| bne .Lnext | |
| lwz r12,0(r9) /* reloc->r_offset */ | |
| lwz r0,8(r9) /* reloc->r_addend */ | |
| add r0,r0,r11 | |
| stwx r0,r11,r12 | |
| .Lnext: add r9,r9,r14 | |
| bdnz 2b | |
| /* Do a cache flush for our text, in case the loader didn't */ | |
| 3: lwz r9,p_start-p_base(r10) /* note: these are relocated now */ | |
| lwz r8,p_etext-p_base(r10) | |
| 4: dcbf r0,r9 | |
| icbi r0,r9 | |
| addi r9,r9,0x20 | |
| cmplw cr0,r9,r8 | |
| blt 4b | |
| sync | |
| isync | |
| /* Clear the BSS */ | |
| lwz r9,p_bss_start-p_base(r10) | |
| lwz r8,p_end-p_base(r10) | |
| li r0,0 | |
| 5: stw r0,0(r9) | |
| addi r9,r9,4 | |
| cmplw cr0,r9,r8 | |
| blt 5b | |
| /* Possibly set up a custom stack */ | |
| lwz r8,p_pstack-p_base(r10) | |
| cmpwi r8,0 | |
| beq 6f | |
| lwz r1,0(r8) | |
| li r0,0 | |
| stwu r0,-16(r1) /* establish a stack frame */ | |
| 6: | |
| #else /* __powerpc64__ */ | |
| /* Save the prom pointer at p_prom. */ | |
| std r5,(p_prom-p_base)(r10) | |
| /* Set r2 to the TOC. */ | |
| ld r2,(p_toc-p_base)(r10) | |
| add r2,r2,r10 | |
| /* Grab the link address of the dynamic section in r11. */ | |
| ld r11,-32768(r2) | |
| cmpwi r11,0 | |
| beq 3f /* if not linked -pie then no dynamic section */ | |
| ld r11,(p_dyn-p_base)(r10) | |
| add r11,r11,r10 | |
| ld r9,(p_rela-p_base)(r10) | |
| add r9,r9,r10 | |
| li r13,0 | |
| li r8,0 | |
| 9: ld r12,0(r11) /* get tag */ | |
| cmpdi r12,0 | |
| beq 12f /* end of list */ | |
| cmpdi r12,RELA | |
| bne 10f | |
| ld r13,8(r11) /* get RELA pointer in r13 */ | |
| b 11f | |
| 10: cmpwi r12,RELASZ | |
| bne .Lcheck_for_relaent | |
| lwz r8,8(r11) /* get RELASZ pointer in r8 */ | |
| b 11f | |
| .Lcheck_for_relaent: | |
| cmpwi r12,RELAENT | |
| bne 11f | |
| lwz r14,8(r11) /* get RELAENT pointer in r14 */ | |
| 11: addi r11,r11,16 | |
| b 9b | |
| 12: | |
| cmpdi r13,0 /* check we have both RELA, RELASZ, RELAENT*/ | |
| cmpdi cr1,r8,0 | |
| beq 3f | |
| beq cr1,3f | |
| cmpdi r14,0 | |
| beq 3f | |
| /* Calcuate the runtime offset. */ | |
| subf r13,r13,r9 | |
| /* Run through the list of relocations and process the | |
| * R_PPC64_RELATIVE ones. */ | |
| divdu r8,r8,r14 /* RELASZ / RELAENT */ | |
| mtctr r8 | |
| 13: ld r0,8(r9) /* ELF64_R_TYPE(reloc->r_info) */ | |
| cmpdi r0,22 /* R_PPC64_RELATIVE */ | |
| bne .Lnext | |
| ld r12,0(r9) /* reloc->r_offset */ | |
| ld r0,16(r9) /* reloc->r_addend */ | |
| add r0,r0,r13 | |
| stdx r0,r13,r12 | |
| .Lnext: add r9,r9,r14 | |
| bdnz 13b | |
| /* Do a cache flush for our text, in case the loader didn't */ | |
| 3: ld r9,p_start-p_base(r10) /* note: these are relocated now */ | |
| ld r8,p_etext-p_base(r10) | |
| 4: dcbf r0,r9 | |
| icbi r0,r9 | |
| addi r9,r9,0x20 | |
| cmpld cr0,r9,r8 | |
| blt 4b | |
| sync | |
| isync | |
| /* Clear the BSS */ | |
| ld r9,p_bss_start-p_base(r10) | |
| ld r8,p_end-p_base(r10) | |
| li r0,0 | |
| 5: std r0,0(r9) | |
| addi r9,r9,8 | |
| cmpld cr0,r9,r8 | |
| blt 5b | |
| /* Possibly set up a custom stack */ | |
| ld r8,p_pstack-p_base(r10) | |
| cmpdi r8,0 | |
| beq 6f | |
| ld r1,0(r8) | |
| li r0,0 | |
| stdu r0,-112(r1) /* establish a stack frame */ | |
| 6: | |
| #endif /* __powerpc64__ */ | |
| /* Call platform_init() */ | |
| bl platform_init | |
| /* Call start */ | |
| b start | |
| #ifdef __powerpc64__ | |
| #define PROM_FRAME_SIZE 512 | |
| .macro OP_REGS op, width, start, end, base, offset | |
| .Lreg=\start | |
| .rept (\end - \start + 1) | |
| \op .Lreg,\offset+\width*.Lreg(\base) | |
| .Lreg=.Lreg+1 | |
| .endr | |
| .endm | |
| #define SAVE_GPRS(start, end, base) OP_REGS std, 8, start, end, base, 0 | |
| #define REST_GPRS(start, end, base) OP_REGS ld, 8, start, end, base, 0 | |
| #define SAVE_GPR(n, base) SAVE_GPRS(n, n, base) | |
| #define REST_GPR(n, base) REST_GPRS(n, n, base) | |
| /* prom handles the jump into and return from firmware. The prom args pointer | |
| is loaded in r3. */ | |
| .globl prom | |
| prom: | |
| mflr r0 | |
| std r0,16(r1) | |
| stdu r1,-PROM_FRAME_SIZE(r1) /* Save SP and create stack space */ | |
| SAVE_GPR(2, r1) | |
| SAVE_GPRS(13, 31, r1) | |
| mfcr r10 | |
| std r10,8*32(r1) | |
| mfmsr r10 | |
| std r10,8*33(r1) | |
| /* remove MSR_LE from msr but keep MSR_SF */ | |
| mfmsr r10 | |
| rldicr r10,r10,0,62 | |
| mtsrr1 r10 | |
| /* Load FW address, set LR to label 1, and jump to FW */ | |
| bcl 20,31,0f | |
| 0: mflr r10 | |
| addi r11,r10,(1f-0b) | |
| mtlr r11 | |
| ld r10,(p_prom-0b)(r10) | |
| mtsrr0 r10 | |
| rfid | |
| 1: /* Return from OF */ | |
| FIXUP_ENDIAN | |
| /* Restore registers and return. */ | |
| rldicl r1,r1,0,32 | |
| /* Restore the MSR (back to 64 bits) */ | |
| ld r10,8*(33)(r1) | |
| mtmsr r10 | |
| isync | |
| /* Restore other registers */ | |
| REST_GPR(2, r1) | |
| REST_GPRS(13, 31, r1) | |
| ld r10,8*32(r1) | |
| mtcr r10 | |
| addi r1,r1,PROM_FRAME_SIZE | |
| ld r0,16(r1) | |
| mtlr r0 | |
| blr | |
| #endif |