
/* loader_stage1.c
 *
 * Copyright (C) 2023 wolfSSL Inc.
 *
 * This file is part of wolfBoot.
 *
 * wolfBoot is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * wolfBoot is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

/* A stage 1 loader to copy wolfBoot from flash to RAM location */

#include "loader.h"
#include "image.h"
#include "hal.h"
#include "spi_flash.h"
#include "printf.h"
#include "wolfboot/wolfboot.h"

#include <string.h>
#include <stdint.h>

#ifndef EXT_FLASH
    #error The stage1 loader only supports use with external flash
#endif

#ifndef WOLFBOOT_STAGE1_SIZE
    #define WOLFBOOT_STAGE1_SIZE (4*1024)
#endif

#ifdef BUILD_LOADER_STAGE1

#if defined(WOLFBOOT_ARCH) && WOLFBOOT_ARCH == PPC
#include "hal/nxp_ppc.h"
#endif

#ifndef DO_BOOT
#ifdef MMU
#define DO_BOOT(addr) do_boot((addr), NULL)
#else
#define DO_BOOT(addr) do_boot((addr))
#endif
#endif

/* requires/assumes inputs and size to be 4-byte aligned */
static void memcpy32(void *dst, const void *src, size_t n)
{
    size_t i;
    const uint32_t *s = (const uint32_t*)src;
    uint32_t *d = (uint32_t*)dst;
    for (i = 0; i < n/4; i++) {
        d[i] = s[i];
    }
}

int main(void)
{
    int ret = -1;
    uint32_t* wolfboot_start;

    hal_init();
    spi_flash_probe(); /* make sure external flash is initialized */

#ifdef BOOT_ROM_ADDR
    /* if this is executing from boot 4KB region (FCM buffer) it must
     * first be relocated to RAM before the eLBC NAND can be read */
    if ((get_pc() & BOOT_ROM_ADDR) == BOOT_ROM_ADDR) {
        wolfboot_start = (uint32_t*)WOLFBOOT_STAGE1_BASE_ADDR;
    #ifdef DEBUG_UART
        uart_write("\nRelocating BOOT ROM to DDR\n", 28);
    #endif

        /* relocate 4KB code to DST and jump */
        memcpy32((void*)wolfboot_start, (void*)BOOT_ROM_ADDR, BOOT_ROM_SIZE);

    #if defined(WOLFBOOT_ARCH) && WOLFBOOT_ARCH == PPC
        /* TODO: Fix hack and consider moving to hal_prepare_boot */
        /* HACK: Fix up stack values modified with trap */
        *((uint32_t*)(WOLFBOOT_STAGE1_BASE_ADDR + 0xC18)) = 0x9421FFF0;
        *((uint32_t*)(WOLFBOOT_STAGE1_BASE_ADDR + 0xC74)) = 0x39200000;

        /* call to relocate code does not return */
        relocate_code(wolfboot_start, (void*)BOOT_ROM_ADDR, BOOT_ROM_SIZE);
    #else
        hal_prepare_boot();
        DO_BOOT(wolfboot_start); /* never returns */
    #endif
    }
#endif

#ifdef DEBUG_UART
    uart_write("Loading wolfBoot to DDR\n", 24);
#endif

    ret = ext_flash_read(
        (uintptr_t)WOLFBOOT_ORIGIN,         /* flash offset */
        (uint8_t*)WOLFBOOT_STAGE1_LOAD_ADDR,/* ram destination */
        BOOTLOADER_PARTITION_SIZE           /* boot-loader partition (entire) */
    );
    if (ret >= 0) {
        wolfboot_start = (uint32_t*)WOLFBOOT_STAGE1_LOAD_ADDR;
    #ifdef PRINTF_ENABLED
        wolfBoot_printf("Jumping to full wolfBoot at %p\n", wolfboot_start);
    #elif defined(DEBUG_UART)
        uart_write("Jumping to full wolfBoot\n", 27);
    #endif

        hal_prepare_boot();
        DO_BOOT(wolfboot_start); /* never returns */
    }

    return 0;
}

#endif /* BUILD_LOADER_STAGE1 */
