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