/* Memory access checker sub functions: use mmap to alloc the bitmaps
   Copyright 1994 Tristan Gingold
		  Written September 1993 by Tristan Gingold

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License 
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   The author may be reached (Email) at the address marc@david.saclay.cea.fr,
   or (US/French mail) as Tristan Gingold 
   			  8 rue Parmentier
   			  F91120 PALAISEAU
   			  FRANCE
*/

/* with Checker, the memory map is like this:
 +----------------------------------------------------------------------------+
 | code | data | bss |  data  | heap | >>>>  |  heap  | stack  | <<<< | stack |
 |      |      |     | bitmap |      | >>>>  | bitmap | bitmap | <<<< |       |
 +----------------------------------------------------------------------------+
 0    &etext       &end          sbrk(0)   MM_HEAP  MM_STACK        %esp    
*/

#include <limits.h>
#include <stddef.h>
#include "malloc.h"
#include "chkrlib.h"

static __ptr_t safe_sbrk(int incr);

#ifdef CHKR_HEAPBITMAP
__ptr_t chkr_heap_begin;
static __ptr_t real_heap_end;
static int heap_bitmap_size;
static uint heap_bitmap_pages;
#endif /* CHKR_HEAPBITMAP */

#ifdef CHKR_DATABITMAP
unsigned char *chkr_data_bitmap;
static int data_bitmap_size;
#endif /* CHKR_DATABITMAP */

#ifdef CHKR_STACKBITMAP
uchar * chkr_stack_bitmap;
static int stack_bitmap_size;
static uint stack_bitmap_pages;
#endif /* CHKR_STACKBITMAP */

int bm_round;			/* number of bytes per byte of bitmap -1 */
int bm_log_size;
static int initialized;

#ifdef CHKR_USE_BITMAP
/* initialize the bitmaps */
void
init_morecore(void)
{
 void *result;
 
 if (initialized)
   return;	/* because, init_morecore can be called by __chkr_init_chkr */
 
 /* initialisation */
 if (bytes_per_state == 0)	/* Can't be used */
   {
     chkr_errno = E_BADBYTEPS;	/* error */
     chkr_perror();
     bytes_per_state = 1;	/* default value */
   }
 while (bytes_per_state != 1)
   {
     bm_log_size++;
     bytes_per_state >>= 1;
   }
 bytes_per_state = 1 << bm_log_size;
 bm_log_size += 2;
 bm_round = (1 << bm_log_size) -1;
 init_mapinfos();
#ifdef CHKR_DATABITMAP
 result = chkr_sbrk(0);
 data_bitmap_size = (result - (__ptr_t)&etext + bm_round) >> bm_log_size;
 chkr_data_bitmap = chkr_sbrk((data_bitmap_size + 0x10) & (~0x0f)); /* alignment */
 if (chkr_data_bitmap == (__ptr_t)-1)
   chkr_data_bitmap = NULL;	/* Won't be checked */
 memset(chkr_data_bitmap, 0xff, data_bitmap_size);
 mapinfo[DATABM]->length = data_bitmap_size << bm_log_size;
 mapinfo[DATABM]->bmbase = chkr_data_bitmap;
#endif /* CHKR_DATABITMAP */
#ifdef CHKR_HEAPBITMAP
 chkr_heap_begin = chkr_sbrk(0);	/* must never fail */
 heap_bitmap_size = 0;
 heap_bitmap_pages = 0;
 mapinfo[HEAPBM]->base = chkr_heap_begin;
 mapinfo[HEAPBM]->length = heap_bitmap_size << bm_log_size;
 mapinfo[HEAPBM]->bmbase = (uchar*)MM_HEAP;
#endif /* CHKR_HEAPBITMAP */
#ifdef CHKR_STACKBITMAP
 if (known_stack_limit)
   stack_bitmapped = known_stack_limit;
 else
   known_stack_limit = stack_bitmapped = &result;
 stack_bitmap_size = (STACK_LIMIT - (int)(&result) + bm_round) >> bm_log_size;
 stack_bitmap_pages = (stack_bitmap_size + PAGESIZE - 1) / PAGESIZE;
 chkr_stack_bitmap = mmap((char*)MM_STACK, 
 	 stack_bitmap_pages * PAGESIZE, 
 	 MM_PROT,
 	 MM_FLAGS,
 	 MM_FILE, 0);
 if (chkr_stack_bitmap == (__ptr_t)-1)
   { 
     chkr_abort();
     chkr_stack_bitmap = NULL;
   }
 /* Rights are set to read/write */
 memset(chkr_stack_bitmap, 0xff, stack_bitmap_size);
 mapinfo[STACKBM]->base = (uchar*)STACK_LIMIT;
 mapinfo[STACKBM]->length = stack_bitmap_size << bm_log_size;
 mapinfo[STACKBM]->bmbase = chkr_stack_bitmap;
#endif
 real_heap_end = chkr_sbrk(0);
 initialized = 1;	/* don't forget this !!! */
}
#endif /* CHKR_USE_BITMAP */

/* sbrk() return the old break address. ie brk(0) == brk(100) */
/* Note that morecore has to take a signed argument so
   that negative values can return memory to the system. */
void *
morecore(int size)
{
 void *result;
#ifdef CHKR_HEAPBITMAP 
 int size1;
 int bm_size;
 static int cache = 4;
#endif /* CHKR_HEAPBITMAP */

#ifdef CHKR_USE_BITMAP
 if(!initialized)
   init_morecore();
#endif /* CHKR_USE_BITMAP */

#if !defined(CHKR_HEAPBITMAP)
 /* really stupid morecore */
 result = chkr_sbrk(size);
 if (result == (void *) -1)
   return NULL;
 return result;
#else
 if (size == 0)
   return safe_sbrk(0);
 if (size > 0)
   {
     result = safe_sbrk(size);
     if (result == NULL)
       return NULL;
     bm_size = (size + bm_round) >> bm_log_size;
     size1 = (heap_bitmap_size + bm_size + PAGESIZE - 1) / PAGESIZE;
     if ( size1 > heap_bitmap_pages)
       {
         if (mmap((char*)(MM_HEAP + heap_bitmap_pages * PAGESIZE),
              (size1 - heap_bitmap_pages) * PAGESIZE,
              MM_PROT, MM_FLAGS, MM_FILE, 0) == (char*)-1)
           chkr_abort();
         /* initialize the new bitmap */
         if (heap_bitmap_size < heap_bitmap_pages * PAGESIZE)
           memset((char*)(MM_HEAP + heap_bitmap_size), 0, bm_size & ~PAGESIZE);
         heap_bitmap_pages = size1;
       }
     else
       {
         /* initialize the new bitmap */
         memset((char*)(MM_HEAP + heap_bitmap_size), 0, bm_size);
       }
     /* Adjust the bitmap info */
     heap_bitmap_size += bm_size;
     mapinfo[HEAPBM]->length = heap_bitmap_size << bm_log_size;
     return result;
   }
 else
   { 
     result = safe_sbrk(size);
     if (result == NULL)
       return NULL;
     bm_size = (abs(size) + bm_round) >> bm_log_size;
     size1 = (heap_bitmap_size - bm_size + PAGESIZE - 1) / PAGESIZE;
     if ( size1 < heap_bitmap_pages && (heap_bitmap_pages - size1) > cache)
       {
         if (munmap((char*)(MM_HEAP + size1 * PAGESIZE),
              (heap_bitmap_pages - size1) * PAGESIZE) != 0)
           chkr_abort();
         /* Adjust the bitmap info */
         heap_bitmap_pages = size1;
       }
     heap_bitmap_size -= bm_size;
     mapinfo[HEAPBM]->length = heap_bitmap_size << bm_log_size;
     return result;
   }
#endif
}

#ifdef CHKR_STACKBITMAP
void
adjust_stackbitmap(__ptr_t ptr)
{
 int diff;
 int size;

 /* Adjust the bitmap */
 stack_bitmap_size = ((uint)STACK_LIMIT - (uint)ptr + bm_round) >> bm_log_size;
 mapinfo[STACKBM]->length = stack_bitmap_size << bm_log_size;
 size = ((uint)STACK_LIMIT - (uint)ptr + PAGESIZE - 1) / PAGESIZE;
 if (size <= stack_bitmap_pages)
   return;	/* we never reduce it */
 diff = size - stack_bitmap_pages;
 if (mmap((char*)(MM_STACK + stack_bitmap_pages * PAGESIZE),
 	  diff * PAGESIZE,
 	  MM_PROT,
 	  MM_FLAGS,
 	  MM_FILE , 0) == (char*)-1)
    chkr_abort();
 stack_bitmap_pages = size;
#if 0
 /* no access right */
 memset((char*)(MM_STACK + stack_bitmap_pages * PAGESIZE), 0, diff * PAGESIZE);
#endif
}
#endif

#ifdef CHKR_HEAPBITMAP
static __ptr_t
safe_sbrk(int incr)
{
 __ptr_t result;
 result = chkr_sbrk(incr);
 if (result == (void*)-1)	/* Is it possible ?? */
   return NULL;
#ifndef CHKR_IS_SAFE
 if (result != real_heap_end)
   {
     chkr_errno = E_CALLBRK;	/* brk(2) has been called */
     chkr_perror();	/* We can't continue. */
     chkr_abort();
   }
 real_heap_end += incr;
#endif
 return result;
}
#endif /* CHKR_HEAPBITMAP */
