#include <exec/types.h>
#include <exec/memory.h>
#include <string.h>

#define __USE_SYSBASE
#include <proto/exec.h>

#include "file.h"
#include "exec.h"
#include "mem.h"
#include "opts.h"

#include "load.h"

static APTR allocation;
static ULONG allocsize;
static BOOL opened;

VOID _STD_loadKernel(VOID)
{
	if (opened) closeFile();
	if (allocation) FreeMem(allocation, allocsize);
}

BOOL loadKernel(STRPTR driver, LONG unit, STRPTR volume, STRPTR name,
                ULONG extra, UBYTE **kp, ULONG *sz, ULONG *en,
                ULONG *sy, ULONG *vp)
{
	struct exec E;
	ULONG ksize, textsize, stringsize;
	UBYTE *mem, *p;
	UWORD kvers;
	ULONG esym = 0;

	if (!openFile(driver,unit,volume, name))
		return FALSE;

	opened = TRUE;

	if (readFile((UBYTE*)&E, sizeof(struct exec)) != sizeof(struct exec))
		fatal("Can't read exec");
	if (E_MAGIC(&E) != NMAGIC)
		fatal("Unknown binary");

	if (options & TEST_ONLY) {
		print("a_text:   "); printaddr(E.a_text);   printchar('\n');
		print("a_data:   "); printaddr(E.a_data);   printchar('\n');
		print("a_bss:    "); printaddr(E.a_bss);    printchar('\n');
		print("a_syms:   "); printaddr(E.a_syms);   printchar('\n');
		print("a_entry:  "); printaddr(E.a_entry);  printchar('\n');
	}

	textsize = (E.a_text + __LDPGSZ - 1) & (-__LDPGSZ);
	ksize    = textsize + E.a_data + E.a_bss;

	/*
	 * get symbol table size & string size
	 * (should check kernel version to see if it will handle it)
	 */
	if ((options & INCLUDE_SYMBOLS) && E.a_syms) {
		if (seekFile(sizeof(E) + E.a_text + E.a_data + E.a_syms) <= 0
		    || readFile((UBYTE*)&stringsize, 4) != 4
		    || seekFile(sizeof(E)) < 0)
			fatal("Can't seek to symbols");
		ksize += sizeof(ULONG) + E.a_syms + stringsize;
	}

	allocsize  = ksize + extra;
	allocation = AllocMem(allocsize, MEMF_ANY);

	mem = allocation;
	if (mem == NULL)
		fatal("No memory for kernel");

	if (readFile(mem, E.a_text) != E.a_text
	    || readFile(mem + textsize, E.a_data) != E.a_data)
	    fatal("Unable to read kernel image");

	kvers = *(UWORD *)(mem + E.a_entry - 2);
	if (kvers == KVERS_OLD) kvers = 0;
	if (kvers > KVERS_MAX)
		fatal("NetBSD loader too old");

	p = mem + textsize + E.a_data;
	bzero(p, E.a_bss);
	p += E.a_bss;

	if ((options & INCLUDE_SYMBOLS) && E.a_syms) {
		*((LONG *)p) = E.a_syms;
		p += sizeof(LONG);
		if (readFile(p, E.a_syms) != E.a_syms)
			fatal("Unable to read symbol table");
		p += E.a_syms;
		if (readFile(p, stringsize) != stringsize)
			fatal("Unable to read symbol strings");
		p += stringsize;
		esym = textsize + E.a_data + E.a_bss
		     + sizeof(ULONG) + E.a_syms + stringsize;
	}

	closeFile();
	opened = FALSE;

	*kp = mem;
	*sz = p - mem;
	*en = E.a_entry;
	*sy = esym;
	*vp = kvers;

	return TRUE;
}
