/*
 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
 *
 * SPDX-License-Identifier: GPL-2.0-only
 */

#include <autoconf.h>
#include <elfloader/gen_config.h>
#include <assembler.h>
#include <armv/assembler.h>

.extern main
.extern non_boot_main

.section ".text.start"

BEGIN_FUNC(_start)
#ifdef CONFIG_ELFLOADER_ARMV8_LEAVE_AARCH64
    /* Include an Aarch64 preamble that switches to aarch32 */
    .incbin "crt0_64.bin"
BEGIN_FUNC(_start32)
#endif // CONFIG_ELFLOADER_ARMV8_LEAVE_AARCH64

    /* If we're not booting under EFI, r2 will contain a pointer to the DTB */
    mov r0, r2

    /* Disable Async aborts that might be pending from bootloader */
    cpsid   ifa

    /* r0 contains the FDT address from the bootloader */
#if CONFIG_MAX_NUM_NODES > 1
    /* Enable SMP */
    mrc     ACTLR(r1)
    orr     r1, r1, #(1 << 6)       /* enable SMP bit   */
#ifdef CONFIG_ARM_CORTEX_A9
    orr     r1, r1, #1              /* enable FW bit    */
#endif
    mcr     ACTLR(r1)
#endif /* CONFIG_MAX_NUM_NODES > 1 */

    ldr sp, =core_stack_alloc + 0xff0

#ifndef CONFIG_IMAGE_EFI
    /*
     * clear the BSS section, since it may not be clear yet.
     * Under EFI, this is done earlier in the boot process.
     */
    push    {r0}
    bl      clear_bss
    pop     {r0}
#endif

    b       main
END_FUNC(_start)

/* Move the elf loader out of the kernel's way */
BEGIN_FUNC(finish_relocation)
#if CONFIG_IMAGE_EFI
    // r0 = offset between current base address and new base address
    // r1 = _DYNAMIC pointer (current base address)
    // r2 = total offset (new_base)
    mov r8, r1 // move to r8

    // figure out relocated address of continue_boot
    adr r1, LC2
    ldmia r1, {r3, r4, r5}

    // r3 = stack pointer (in current program)
    // r4 = continue_boot (in current program)
    // r5 = current base address (_text, the start of the binary)

    sub sp, r3, r0                  // point sp to new stack
    sub r4, r4, r0                  // continue_boot address in relocated elfloader

    // self_reloc expects:
    // r0 = current base address
    // r1 = dynamic section pointer
    // r2 = new base address
    // r3 = 0

    mov r0, r5                      // current base address
    mov r1, r8                      // put _DYNAMIC back
    // r2 is new base address, passed from relocate_below_kernel()
    mov r3, #0                      // NULL r3

    push {r4}                       // save continue_boot address

    bl _relocate

    pop {r4}
    mov r0, #1
    blx r4
    b abort // should never get here!
#else
    /* On non-EFI, the image cannot be relocated.
     * If you hit this, you probably need to modify the ELF loader
     * load address.
     */
    mov pc, lr
#endif
END_FUNC(finish_relocation)
.align 2
.type LC2, #object
LC2:
.word core_stack_alloc + 0xff0  // r3
.word continue_boot             // r4
.word _text                     // r5
.size LC2, . - LC2

#if CONFIG_MAX_NUM_NODES > 1
BEGIN_FUNC(secondary_startup)
    /* Invalidate caches before proceeding... */
    mov     r0, #0
    mcr     IIALL(r0)
    dcache  isw

    /* Disable Async aborts that might be pending from bootloader */
    cpsid   ifa

    /* Enable SMP */
    mrc     ACTLR(r0)
    orr     r0, r0, #(1 << 6)       /* enable SMP bit   */
#ifdef CONFIG_ARM_CORTEX_A9
    orr     r0, r0, #1              /* enable FW bit    */
#endif
    mcr     ACTLR(r0)

    /*
     * secondary_data is a struct like this:
     * 0x0 void *entry
     * 0x4 void *stack
     */
    ldr     r0, =secondary_data
    ldr     r1, [r0, #0x4]         /* load stack */
    mov     sp, r1

    ldr     r2, [r0]               /* load entry point */

    /* core_entry expects sp as its first argument */
    mov     r0, r1
    bx r2
END_FUNC(secondary_startup)
#endif /* CONFIG_MAX_NUM_NODES */

/*
 * Symbols required for libgcc.
 */
.global raise
.global __aeabi_unwind_cpp_pr0
.global __aeabi_unwind_cpp_pr1
.global __aeabi_unwind_cpp_pr2
raise:
__aeabi_unwind_cpp_pr0:
__aeabi_unwind_cpp_pr1:
__aeabi_unwind_cpp_pr2:
    b       raise
