/**
 * OVM Interpreter (c) 2001-2002 S3 Lab, Purdue University
 *
 * This file contains an implementation of the image loader.
 *
 * @file boot.c 
 * @author Christian Grothoff
 * @author James Liang
 * @author Ben L. Titzer
 * @author Hiroshi Yamauchi
 **/

#include "boot.h"

/**
 * Debug boot.c?
 **/
#define DEBUG_BOOT_C YES

/* ************************** helper methods ******************** */

#ifndef BOOTBASE
/**
 * Check the magic value given as a parameter. Print an error message if it does not
 * match and exit.
 * @param value the value to check
 * @param message message to print
 **/
static void check_magic(int value, char * message) {
  if (IMAGE_MAGIC == value)  return;

  else if (ntohl(IMAGE_MAGIC) == (unsigned int)value) 
    errexit("Image is in wrong endianness (MAGIC error: %s)\n", message);
  else 
    errexit("0x%08X does not match expected IMAGE_MAGIC 0x%08X (%s)\n",
	    value, IMAGE_MAGIC, message);
}

/**
 * Compute the total size of an image based on the used memory reported in the
 * header, the size of the header, and the computed size of the reference map.
 **/
static size_t compute_image_size(int usedMem) {
/*    int t6 = usedMem / 4; */
/*    int t3 = ( t6 / 8) +  1; */
  // int t = t2 + sizeof(ImageFormat) + 8;
    ASSERT(usedMem > 0, "negative value for usedMem");
    return (size_t)usedMem + sizeof(ImageFormat) ;// t;

}
#endif

/* ************************** public methods ******************** */

static ImageFormat *image;

void *getImageBaseAddress() {
  return image->data + 4;	   /* first word is null */
}

void *getImageEndAddress() {
  int off = image->usedMemory - 4; /* last word is magic number */
  return image->data + off;
}

/**
 * Load bootimage from file (should only be called once).
 * @param filename the name of the bootimage-file
 * @param mainObject this is set to the address of the main object
 * @param mainMethod this is set to the ByteCode that is executed first
 * @return OK on success, SYSERR on error (some errors also call errexit)
 **/
int load_image(char * filename,
	       void ** mainObject,
	       ByteCode ** mainMethod,
	       struct ovm_core_execution_CoreServicesAccess ** execCSA,
	       struct arr_jbyte ** utf8Array,
	       ByteCode ** bottomMethod,
	       jref *bootContext) {
#ifndef BOOTBASE
  int fileDescriptor;
  size_t length;
  ImageFormat imageHeader;
  void* baseAddress;
  int pgsize;

#if DEBUG_BOOT_C == YES
  print("Loading image from file %s...\n",filename);
#endif

  pgsize = getpagesize();
  /** try to open the file **/
  fileDescriptor = open(filename, O_NDELAY|O_RDONLY);
  ASSERT(fileDescriptor >= 0,"Could not open file with the bootimage.\n");

  /** read in the header of the image **/
  length = read(fileDescriptor, &imageHeader, sizeof(ImageFormat));
  ASSERT(sizeof(ImageFormat) == length, "Could not read full image header!");

  /** check magic number **/
  check_magic(imageHeader.OVM_MAGIC, 
	      "Magic number of image does not match (1)");

  /** look at the base address it expects **/
  baseAddress = (void*) imageHeader.baseAddress;

  /** find out the size of the entire image **/
  length = lseek(fileDescriptor,0,SEEK_END);
  ASSERT((off_t)length > 0, "Couldn't determine image size");

  /* Round up request to a multiple of page size.  The remainder of
   * the last page will be zero-filled.  Not * rounding up may cause
   * the OS to round down.
   */
  length = (length + pgsize - 1) & ~(pgsize - 1);
  image = mmap(baseAddress,
	       length,
	       PROT_READ|PROT_WRITE,
	       MAP_PRIVATE,
	       fileDescriptor,
	       0);

  close(fileDescriptor);

  ASSERT(image == baseAddress,
	 "Could not mmap bootimage at base address");

  registerMem(image, length);

#if DEBUG_BOOT_C == YES
  printf(" Size of image       : %d bytes\n",image->usedMemory);
  printf(" Image Base Address  : 0x%0X\n",(int) baseAddress);
  printf(" Main object location: 0x%0X\n",(int) image->mainObject);
  printf(" Main method location: 0x%0X\n",(int) image->mainMethod);
#endif

  if (length != compute_image_size(image->usedMemory))
      /* On OSX size_t is not unsigned int but unsigned long int - hence the casts */
    fprintf(stderr, "computed image size and actual size mismatch! %u != %u\n",
	   (unsigned)length, (unsigned)compute_image_size(image->usedMemory));      
#else
  // malloc starts by returning memory from the last page of the
  // bootimage, this is bad.
  malloc(getpagesize());
  image = (ImageFormat *) BOOTBASE;
#endif

  *mainObject = (void *)image->mainObject;
  *mainMethod = (ByteCode *)image->mainMethod;
  *execCSA = image->coreServicesAccess; 
  // *utf8Array = ((struct ovm_core_repository_Repository*)image->repository)->utf8s_->utf8s_;
  *bootContext = (jref)image->bootContext;
  *bottomMethod = (ByteCode* ) image->bottomFrameCode;
  return OK;

} // end of boot
