/*
 *
 *       <elf-loader.c>
 *
 *       ELF file loader
 *
 *   Copyright (C) 2009 Laurent Vivier (Laurent@vivier.eu)
 *
 *   Some parts Copyright (C) 2002, 2003, 2004 Samuel Rydh (samuel@ibrium.se)
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   version 2
 *
 */

#include "openbios/config.h"
#include "openbios/bindings.h"
#include "modules.h"
#include "ofmem.h"

#include "openbios/elf.h"
#include "asm/elf.h"

/* TODO: manage ELF notes section */

//#define DEBUG_ELF

#ifdef DEBUG_ELF
#define DPRINTF(fmt, args...) \
    do { printk("%s: " fmt, __func__ , ##args); } while (0)
#else
#define DPRINTF(fmt, args...) \
    do { } while (0)
#endif

DECLARE_NODE(elf_loader, INSTALL_OPEN, 0, "+/packages/elf-loader" );

#ifdef CONFIG_PPC
extern void             flush_icache_range( char *start, char *stop );
#endif

static void
elf_loader_init_program( void *dummy )
{
	char *base;
	int i;
	Elf_ehdr *ehdr;
	Elf_phdr *phdr;
	size_t size;
	char *addr;

	feval("0 state-valid !");

	feval("load-base");
	base = (char*)POP();

	ehdr = (Elf_ehdr *)base;
	phdr = (Elf_phdr *)(base + ehdr->e_phoff);

	for (i = 0; i < ehdr->e_phnum; i++) {
		DPRINTF("filesz: %08lX memsz: %08lX p_offset: %08lX "
                        "p_vaddr %08lX\n",
			(ulong)phdr[i].p_filesz, (ulong)phdr[i].p_memsz,
			(ulong)phdr[i].p_offset, (ulong)phdr[i].p_vaddr );

		size = MIN(phdr[i].p_filesz, phdr[i].p_memsz);
		if (!size)
			continue;
		if( ofmem_claim( phdr[i].p_vaddr, phdr[i].p_memsz, 0 ) == -1 ) {
                        printk("Claim failed!\n");
			return;
		}
		addr = (char*)phdr[i].p_vaddr;
		memcpy(addr, base + phdr[i].p_offset, size);
#ifdef CONFIG_PPC
		flush_icache_range( addr, addr + size );
#endif
	}
	/* FIXME: should initialize saved-program-state. */
	PUSH(ehdr->e_entry);
	feval("elf-entry !");
	feval("-1 state-valid !");
}

NODE_METHODS( elf_loader ) = {
	{ "init-program", elf_loader_init_program },
};

void elf_loader_init( void )
{
	REGISTER_NODE( elf_loader );
}
