/*
   MMHeapMemory.c, Copyright  (c) by George Fankhauser,
   Swiss Federal Institute of Technology,
   Computer Engineering and Networks Laboratory.
 
   TOPSY -- A Teachable Operating System.
   Implementation of a tiny and simple
   micro kernel for teaching purposes.
 
   For further information, please visit http://www.tik.ee.ethz.ch/~topsy
 
   This software is provided under the terms of the GNU General Public Licence.
 
   A full copy of the GNU GPL is provided in the file COPYING found in the 
   development root of Topsy.
 */
/*
	File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Memory/MMHeapMemory.c,v $
	Author(s):             George Fankhauser
	Affiliation:           ETH Zuerich, TIK
	Version:               $Revision: 2922 $
	Creation Date:         
	Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $      by: $Author: baderma $
*/

#include "Topsy.h"
#include "Configuration.h"
#include "Threads.h"
#include "Syscall.h"
#include "MMHeapMemory.h"
#include "MMVirtualMemory.h"
#include "MMMapping.h"
#include "Lock.h"
#include "Support.h"
#include "System.h"


typedef enum {HM_FREED, HM_ALLOCATED} AreaStatus;

typedef struct HmEntryDesc_t
{
  AreaStatus          		status;
  struct HmEntryDesc_t*   	next;
}
HmEntryDesc;

typedef HmEntryDesc* HmEntry;

static HmEntry start = NULL;
static HmEntry end = NULL;

/* internal function prototypes */
static HmEntry findFree(unsigned long size);
void split(HmEntry entry, unsigned long int);
void compact(HmEntry at);

/* a lock is needed in order to keep the heap pointers consistent. only
 * one kernel thread may be in hmAlloc and hmFree at a time
 */
static LockDesc hmLockDesc;
static Lock hmLock;

#define ALIGN 4
#define LEFTOVER (ALIGN*2)
#define ENTRYSIZE(ptr) ((unsigned long)(ptr->next) - \
    (unsigned long)ptr - sizeof(HmEntryDesc))

//-CJ static int xsharp = 0; /* only used if TST defined */


//-LR #define TST(x) if (xsharp) {printf("--%s\n",x); TSThlt();}
#define TST(x)

Error hmInit(Address addr)
{
  TST("hmInit");

  hmInitCalled();

  start = (HmEntry)addr;
  start->next = (HmEntry)((unsigned long)addr + KERNELHEAPSIZE -
                          sizeof(HmEntryDesc));
  //-LR   start->next = (HmEntry)((unsigned long)addr + KERNELHEAPSIZE +
  //-LR       sizeof(HmEntryDesc));
  start->status = HM_FREED;

  end = start->next;
  end->next = NULL;
  end->status = HM_ALLOCATED;

  hmLock = &hmLockDesc;
  lockInit( hmLock);

#if 0

  printf("hmInit: About to exit\n");
  printf("  start:       0x%8x\n", (int)start);
  printf("       ->next: 0x%8x\n", (int)start->next);
  printf("  end:         0x%8x\n", (int)end);
  printf("       ->next: 0x%8x\n", (int)end->next);
  printf("  size:          %8i\n", ENTRYSIZE(start));
  //-LR   BRK();
#endif

  return HM_INITOK;
}


Error hmAlloc(Address* addressPtr, unsigned long int size)
{
  HmEntry entry;

  TST("hmAlloc");

  hmAllocCalled();

  //-LR   if (xx_was_in_tmMain) {
  //-LR     ERROR("hmAlloc");
  //-LR     BRK();
  //-LR   }

  if (size == 4)
    WARNING("Four bytes allocated. Possible pointer problem?");

  /* The following definition is wrong:
   *    size = 8
   * -> size = 8 + (4 - (8 % 4 = 0)) = 8 + 4 = 12 
   * instead of 8!!!
   */
  //-BL size = size + (ALIGN - size % ALIGN);   /* round up to multiple of words */
  size = ((size + 3) >> 2 ) << 2;		// this should round properly to words
  entry = start;
  *addressPtr = NULL;

  /*** here we enter a critical section */
  lock(hmLock);

  //-LR   if (xx_was_in_tmMain)
  //-LR     ERROR("After hmLock");

  /* try to find a free piece of memory */
  entry = findFree(size);
  if (entry == NULL)
  {
    //-LR     if (xx_was_in_tmMain)
    //-LR       ERROR("hmAlloc is about compact()");
    /* compact and try again */
    compact(start);
    entry = findFree(size);
  }
  if (entry == NULL)
  {
    //-LR     if (xx_was_in_tmMain)
    //-LR     Debug("hmAlloc is failing\n");
    unlock( hmLock);
    hmAllocFailed();
    return HM_ALLOCFAILED;
  }
  split(entry, size);
  *addressPtr = (Address)((unsigned long)entry + sizeof(HmEntryDesc));

  unlock(hmLock);
  //-LR   if (xx_was_in_tmMain)
  //-LR     ERROR("Correclty returning");
  return HM_ALLOCOK;
}


Error hmFree(Address address)
{
  HmEntry  entry, addressEntry;

  TST("hmFree");

  hmFreeCalled();
  /* check first if pointer is inside the heap */
  if (((unsigned long)start > (unsigned long)address) ||
      ((unsigned long)address >= ((unsigned long)end - LEFTOVER)))
  {
    WARNING("hmFree got non-heap pointer");
    hmFreeFailed();
    return HM_FREEFAILED;
  }
  /*** here we enter a critical section */
  // XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
  // XXX         here we sometimes enter a deadlock          XXX
  // XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX

  lock(hmLock);

  entry = start;
  addressEntry = (HmEntry)((unsigned long)address - sizeof(HmEntryDesc));
  while (entry != NULL)
  {
    if (entry == addressEntry)
      break;
    entry = entry->next;
  }
  if (entry == NULL)
  {
    unlock( hmLock);
    hmFreeFailed();
    return HM_FREEFAILED;
  }

  entry->status = HM_FREED;
  unlock( hmLock);

  return HM_FREEOK;
}

HmEntry findFree(unsigned long size)
{
  HmEntry entry = start;

  findFreeCalled();

  while (entry != NULL)
  {
    //-LR     printf("entry = 0x%8x; ENTRYSIZE(entry) = 0x%8x; size = 0x%8x\n",
    //-LR         (int)entry, (int)ENTRYSIZE(entry), (int)size);

    if (entry->status == HM_FREED && ENTRYSIZE(entry) >= size)
      break;
    entry = entry->next;
  }
  /* LR 000604 */
  //-LR   printf("MMHeapMemory.c/findFree: size = 0x%8x, entry = 0x%8x\n", (int)size, (int)entry);
  //-LR asm("hlt");
  return entry;
}


void split(HmEntry entry, unsigned long int size)
{
  HmEntry new;

  splitCalled();
  TST("split");
  //-LR   printf("split: entry = 0x%8x; e-size = 0x%8x; ", entry, ENTRYSIZE(entry));

  if (ENTRYSIZE(entry) >= (size + sizeof(HmEntryDesc) + LEFTOVER))
  {
    new = (HmEntry)((unsigned long)(entry) + sizeof(HmEntryDesc) + size);
    new->next = entry->next;
    new->status = HM_FREED;
    entry->next = new;
  }
  entry->status = HM_ALLOCATED;

  //-LR   printf("entry = 0x%8x; e-size = 0x%8x; ", entry, ENTRYSIZE(entry));
  //-LR   printf("new = 0x%8x; new->next = 0x%8x; \n",
  //-LR       (int)entry, (int)new, (int)new->next);

  TST("split");
}


void compact(HmEntry at)
{
  HmEntry atNext;

  compactCalled();

  while (at != NULL)
  {
    atNext = at->next;
    while ((at->status == HM_FREED) && (atNext != NULL))
    {
      if (atNext->status != HM_FREED)
        break;
      at->next = atNext->next;     /* collapse two free entries into one */
      atNext = atNext->next;
    }
    at = at->next;
  }
}

void CheckHM()
{
  HmEntry entry;
  int i = 0;

  entry = start;

  while (entry)
  {
    if (i && i % 5 == 0)
      printf("\n");
    printf("%2i:0x%8x ",i++, (unsigned int)entry);
    entry = entry->next;
  }
  printf("\n");
  //-LR   BRK();
}

