/*
 * Copyright (c) 2001 William L. Pitts
 * Modifications (c) 2004 Felix Domke
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are freely
 * permitted provided that the above copyright notice and this
 * paragraph and the following disclaimer are duplicated in all
 * such forms.
 *
 * This software is provided "AS IS" and without any express or
 * implied warranties, including, without limitation, the implied
 * warranties of merchantability and fitness for a particular
 * purpose.
 */

#include <stdio.h>
#include <string.h>

#include <gccore.h>

#include "elf_abi.h"

/* ======================================================================
 * Determine if a valid ELF image exists at the given memory location.
 * First looks at the ELF header magic field, the makes sure that it is
 * executable and makes sure that it is for a PowerPC.
 * ====================================================================== */
s32 valid_elf_image(void *addr) {
    Elf32_Ehdr *ehdr; /* Elf header structure pointer */
    
    ehdr = (Elf32_Ehdr *) addr;
    if (!IS_ELF (*ehdr)) return 0;
    if (ehdr->e_type != ET_EXEC) return -1;
    if (ehdr->e_machine != EM_PPC) return -1;
    
    return 1;
}

/* ======================================================================
 * A very simple elf loader, assumes the image is valid, returns the
 * entry point address.
 * ====================================================================== */
u32 load_elf_image(void *addr) {
    Elf32_Ehdr *ehdr;
    Elf32_Shdr *shdr;
    u8 *strtab = 0;
    u8 *image;
    int i;

    ehdr = (Elf32_Ehdr *) addr;
    /* Find the section header string table for output info */
    shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + (ehdr->e_shstrndx
            * sizeof(Elf32_Shdr)));

    if (shdr->sh_type == SHT_STRTAB) strtab = (u8 *) (addr + shdr->sh_offset);

    /* Load each appropriate section */
    for (i = 0; i < ehdr->e_shnum; ++i) {
        //printf("i %d of %d\n", i, ehdr->e_shnum);
        shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + (i * sizeof(Elf32_Shdr)));

        if (!(shdr->sh_flags & SHF_ALLOC) || shdr->sh_addr == 0
                || shdr->sh_size == 0) {
            continue;
        }

        shdr->sh_addr &= 0x3FFFFFFF;
        shdr->sh_addr |= 0x80000000;

        if (strtab) {
            /*printf("%sing section %s @ 0x%08x (0x%08x bytes)\n", (shdr->sh_type
                    == SHT_NOBITS) ? "clear" : "load", &strtab[shdr->sh_name],
                (u32) shdr->sh_addr, (u32) shdr->sh_size);*/
        }

        if (shdr->sh_type == SHT_NOBITS) {
            memset((void *) shdr->sh_addr, 0, shdr->sh_size);
        } else {
            image = (u8 *) addr + shdr->sh_offset;
            memcpy((void *) shdr->sh_addr, (const void *) image, shdr->sh_size);
        }
    }
    for (i = 0; i < ehdr->e_shnum; ++i) {
        //printf("i %d of %d\n", i, ehdr->e_shnum);
        shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + (i * sizeof(Elf32_Shdr)));

        if (!(shdr->sh_flags & SHF_ALLOC) || shdr->sh_addr == 0
                || shdr->sh_size == 0) {
            continue;
        }

        shdr->sh_addr &= 0x3FFFFFFF;
        shdr->sh_addr |= 0x80000000;
        
        DCFlushRangeNoSync((void *) shdr->sh_addr, shdr->sh_size);
    }

    return (ehdr->e_entry & 0x3FFFFFFF) | 0x80000000;
}

