root/arch/powerpc/boot/crt0.S
/* 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