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

#include <autoconf.h>
#include <elfloader/gen_config.h>
#ifdef CONFIG_IMAGE_BINARY
#include <image_start_addr.h>
#endif

#include <assembler.h>

.extern main

.section ".text.start"
BEGIN_FUNC(_start)
    adrp    x19, core_stack_alloc
    add     x19, x19, #0xff0
    mov     sp, x19
#ifdef CONFIG_IMAGE_BINARY
    stp     x0, x1, [sp, #-16]!
    bl      clear_bss
    /*
     * Binary images may not be loaded in the correct location.
     * Try and move ourselves so we're in the right place.
     */
    bl      fixup_image_base
    mov     x2, x0
    /* restore original arguments for next step */
    ldp     x0, x1, [sp, #-16]!
    /* fixup_image_base returns 1 if no need to move */
    cmp     x2, #1
    beq     1f
    /* otherwise, jump to the start of the new elfloader */

    br      x2
1:
#endif
    b       main
END_FUNC(_start)

#ifdef CONFIG_IMAGE_BINARY
BEGIN_FUNC(fixup_image_base)
    stp     x29, x30, [sp, #-16]!
    mov     x29, sp
    ldr     x0, =IMAGE_START_ADDR
    adr     x1, _start
    cmp     x0, x1
    beq     image_ok

    adrp    x2, _end
    add     x2, x2, #:lo12:_end
    sub     x2, x2, x1

    /* sanity check: don't want to overwrite ourselves! we assume
     * everything between _start and _archive_start is important
     * (i.e. code that might be run while relocating)
     * but allow overlap for things after _archive_start.
     */
    adrp    x3, _archive_start
    add     x3, x3, #:lo12:_archive_start

    add     x4, x0, x2 /* Dest end */

    /* check: if (end < archive_start && end >= _start) { abort } */
    cmp     x4, x3
    bge     1f

    cmp     x4, x1
    blt     1f

    b       cant_reloc

1:
    /* check: if (dest < archive_start && dest >= _start) { abort } */
    cmp     x0, x3
    bge     2f

    cmp     x0, x1
    blt     2f

cant_reloc:
    b abort

2:
    /* x0 = desired image base */
    /* x1 = current image space */
    /* x2 = image size */
    bl      memmove
    /* x0 = dest, save it to a callee-saved register while we invalidate icache */
    mov     x19, x0
    bl      flush_dcache
    bl      invalidate_icache
    mov     x0, x19
    b 1f

image_ok:
    /* already in the right place, just keep booting */
    mov x0, #1
1:
    ldp     x29, x30, [sp], #16
    ret
END_FUNC(fixup_image_base)
#endif

/* Move the elf loader out of the kernel's way */
BEGIN_FUNC(finish_relocation)
    /*
     * On aarch64 the kernel is loaded at a very high address:
     * at least above 0x0000ff8080000000. We assume that
     * the ELF loader will never get loaded in a way that overlaps
     * with the kernel, so reaching this function is an error.
     */
    b abort // should never get here!
END_FUNC(finish_relocation)

/* secondary cpu startup */
BEGIN_FUNC(secondary_startup)
    /*
     * secondary_data is a struct that looks like this:
     * 0x0 void *entry
     * 0x8 void *stack
     */
    adrp    x19, secondary_data
    add     x19, x19, #:lo12:secondary_data

    ldr     x0, [x19, #0x8]     // load stack
    mov     sp, x0
    ldr     x1, [x19, #0x0]     // load entry point

    br x1
END_FUNC(secondary_startup)
