/* This file is concerned with allocating and freeing arbitrary-size blocks of
 * physical memory on behalf of the FORK and EXEC system calls.  The key data
 * structure used is the hole table, which maintains a list of holes in memory.
 * It is kept sorted in order of increasing memory address. The addresses
 * it contains refers to physical memory, starting at absolute address 0
 * (i.e., they are not relative to the start of PM).  During system
 * initialization, that part of memory containing the interrupt vectors,
 * kernel, and PM are "allocated" to mark them as not available and to
 * remove them from the hole list.
 *
 * The entry points into this file are:
 *   alloc_mem:	allocate a given sized chunk of memory
 *   free_mem:	release a previously allocated chunk of memory
 *   mem_init:	initialize the tables when PM start up
 */

#define _SYSTEM 1

#include <minix/com.h>
#include <minix/callnr.h>
#include <minix/type.h>
#include <minix/config.h>
#include <minix/const.h>
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/debug.h>
#include <minix/bitmap.h>

#include <sys/mman.h>

#include <limits.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <memory.h>

#include "vm.h"
#include "proto.h"
#include "util.h"
#include "glo.h"
#include "pagerange.h"
#include "addravl.h"
#include "sanitycheck.h"
#include "memlist.h"

/* AVL tree of free pages. */
addr_avl addravl;

/* Used for sanity check. */
PRIVATE phys_bytes mem_low, mem_high;
#define assert_range(addr, len)  			\
	assert((addr) >= mem_low);			\
	assert((addr) + (len) - 1 <= mem_high);

struct hole {
	struct hole *h_next;          /* pointer to next entry on the list */
	phys_clicks h_base;           /* where does the hole begin? */
	phys_clicks h_len;            /* how big is the hole? */
	int freelist;
	int holelist;
};

static int startpages;


#define _NR_HOLES (_NR_PROCS*2)  /* No. of memory holes maintained by VM */

PRIVATE struct hole hole[_NR_HOLES];

PRIVATE struct hole *hole_head;	/* pointer to first hole */
PRIVATE struct hole *free_slots;/* ptr to list of unused table slots */

FORWARD _PROTOTYPE( void del_slot, (struct hole *prev_ptr, struct hole *hp) );
FORWARD _PROTOTYPE( void merge, (struct hole *hp)			    );
FORWARD _PROTOTYPE( void free_pages, (phys_bytes addr, int pages)	    );
FORWARD _PROTOTYPE( phys_bytes alloc_pages, (int pages, int flags,
	phys_bytes *ret));

#if SANITYCHECKS
FORWARD _PROTOTYPE( void holes_sanity_f, (char *fn, int line)		    );
#define CHECKHOLES holes_sanity_f(__FILE__, __LINE__)

#define PAGESPERGB (1024*1024*1024/VM_PAGE_SIZE) /* 1GB of memory */
#define MAXPAGES (2*PAGESPERGB)
#define CHUNKS BITMAP_CHUNKS(MAXPAGES)
PRIVATE bitchunk_t pagemap[CHUNKS];

#else
#define CHECKHOLES 
#endif

#if SANITYCHECKS

/*===========================================================================*
 *				holes_sanity_f				     *
 *===========================================================================*/
PRIVATE void holes_sanity_f(file, line)
char *file;
int line;
{
#define myassert(c) { \
  if(!(c)) { \
	printf("holes_sanity_f:%s:%d: %s failed\n", file, line, #c); \
	util_stacktrace();	\
	panic("assert failed"); } \
  }	

	int h, c = 0, n = 0;
	struct hole *hp;

	/* Reset flags */
	for(h = 0; h < _NR_HOLES; h++) {
		hole[h].freelist = 0;
		hole[h].holelist = 0;
	}

	/* Mark all holes on freelist. */
	for(hp = free_slots; hp; hp = hp->h_next) {
		myassert(!hp->freelist);
		myassert(!hp->holelist);
		hp->freelist = 1;
		myassert(c < _NR_HOLES);
		c++;
		n++;
	}

	/* Mark all holes on holelist. */
	c = 0;
	for(hp = hole_head; hp; hp = hp->h_next) {
		myassert(!hp->freelist);
		myassert(!hp->holelist);
		hp->holelist = 1;
		myassert(c < _NR_HOLES);
		c++;
		n++;
	}

	/* Check there are exactly the right number of nodes. */
	myassert(n == _NR_HOLES);

	/* Make sure each slot is on exactly one of the list. */
	c = 0;
	for(h = 0; h < _NR_HOLES; h++) {
		hp = &hole[h];
		myassert(hp->holelist || hp->freelist);
		myassert(!(hp->holelist && hp->freelist));
		myassert(c < _NR_HOLES);
		c++;
	}

	/* Make sure no holes overlap. */
	for(hp = hole_head; hp && hp->h_next; hp = hp->h_next) {
		myassert(hp->holelist);
		hp->holelist = 1;
		/* No holes overlap. */
		myassert(hp->h_base + hp->h_len <= hp->h_next->h_base);

		/* No uncoalesced holes. */
		myassert(hp->h_base + hp->h_len < hp->h_next->h_base);
	}
}
#endif

/*===========================================================================*
 *				alloc_mem				     *
 *===========================================================================*/
PUBLIC phys_clicks alloc_mem(phys_clicks clicks, u32_t memflags)
{
/* Allocate a block of memory from the free list using first fit. The block
 * consists of a sequence of contiguous bytes, whose length in clicks is
 * given by 'clicks'.  A pointer to the block is returned.  The block is
 * always on a click boundary.  This procedure is called when memory is
 * needed for FORK or EXEC.
 */
  register struct hole *hp, *prev_ptr;
  phys_clicks old_base, mem = NO_MEM, align_clicks = 0;
  int s;

  if(memflags & PAF_ALIGN64K) {
  	align_clicks = (64 * 1024) / CLICK_SIZE;
	clicks += align_clicks;
  }

  mem = alloc_pages(clicks, memflags, NULL);
  if(mem == NO_MEM) {
    free_yielded(clicks * CLICK_SIZE);
    mem = alloc_pages(clicks, memflags, NULL);
  }

  if(mem == NO_MEM)
  	return mem;

CHECKHOLES;

  if(align_clicks) {
  	phys_clicks o;
  	o = mem % align_clicks;
  	if(o > 0) {
  		phys_clicks e;
  		e = align_clicks - o;
	  	free_mem(mem, e);
	  	mem += e;
	}
  }
CHECKHOLES;

  return mem;
}

/*===========================================================================*
 *				free_mem				     *
 *===========================================================================*/
PUBLIC void free_mem(phys_clicks base, phys_clicks clicks)
{
/* Return a block of free memory to the hole list.  The parameters tell where
 * the block starts in physical memory and how big it is.  The block is added
 * to the hole list.  If it is contiguous with an existing hole on either end,
 * it is merged with the hole or holes.
 */
  register struct hole *hp, *new_ptr, *prev_ptr;
CHECKHOLES;

  if (clicks == 0) return;

  assert(CLICK_SIZE == VM_PAGE_SIZE);
  free_pages(base, clicks);
  return;

  if ( (new_ptr = free_slots) == NULL) 
  	panic("hole table full");
  new_ptr->h_base = base;
  new_ptr->h_len = clicks;
  free_slots = new_ptr->h_next;
  hp = hole_head;

  /* If this block's address is numerically less than the lowest hole currently
   * available, or if no holes are currently available, put this hole on the
   * front of the hole list.
   */
  if (hp == NULL || base <= hp->h_base) {
	/* Block to be freed goes on front of the hole list. */
	new_ptr->h_next = hp;
	hole_head = new_ptr;
	merge(new_ptr);
CHECKHOLES;
	return;
  }

  /* Block to be returned does not go on front of hole list. */
  prev_ptr = NULL;
  while (hp != NULL && base > hp->h_base) {
	prev_ptr = hp;
	hp = hp->h_next;
  }

  /* We found where it goes.  Insert block after 'prev_ptr'. */
  new_ptr->h_next = prev_ptr->h_next;
  prev_ptr->h_next = new_ptr;
  merge(prev_ptr);		/* sequence is 'prev_ptr', 'new_ptr', 'hp' */
CHECKHOLES;
}

/*===========================================================================*
 *				del_slot				     *
 *===========================================================================*/
PRIVATE void del_slot(prev_ptr, hp)
/* pointer to hole entry just ahead of 'hp' */
register struct hole *prev_ptr;
/* pointer to hole entry to be removed */
register struct hole *hp;	
{
/* Remove an entry from the hole list.  This procedure is called when a
 * request to allocate memory removes a hole in its entirety, thus reducing
 * the numbers of holes in memory, and requiring the elimination of one
 * entry in the hole list.
 */
  if (hp == hole_head)
	hole_head = hp->h_next;
  else
	prev_ptr->h_next = hp->h_next;

  hp->h_next = free_slots;
  hp->h_base = hp->h_len = 0;
  free_slots = hp;
}

/*===========================================================================*
 *				merge					     *
 *===========================================================================*/
PRIVATE void merge(hp)
register struct hole *hp;	/* ptr to hole to merge with its successors */
{
/* Check for contiguous holes and merge any found.  Contiguous holes can occur
 * when a block of memory is freed, and it happens to abut another hole on
 * either or both ends.  The pointer 'hp' points to the first of a series of
 * three holes that can potentially all be merged together.
 */
  register struct hole *next_ptr;

  /* If 'hp' points to the last hole, no merging is possible.  If it does not,
   * try to absorb its successor into it and free the successor's table entry.
   */
  if ( (next_ptr = hp->h_next) == NULL) return;
  if (hp->h_base + hp->h_len == next_ptr->h_base) {
	hp->h_len += next_ptr->h_len;	/* first one gets second one's mem */
	del_slot(hp, next_ptr);
  } else {
	hp = next_ptr;
  }

  /* If 'hp' now points to the last hole, return; otherwise, try to absorb its
   * successor into it.
   */
  if ( (next_ptr = hp->h_next) == NULL) return;
  if (hp->h_base + hp->h_len == next_ptr->h_base) {
	hp->h_len += next_ptr->h_len;
	del_slot(hp, next_ptr);
  }
}

/*===========================================================================*
 *				mem_init				     *
 *===========================================================================*/
PUBLIC void mem_init(chunks)
struct memory *chunks;		/* list of free memory chunks */
{
/* Initialize hole lists.  There are two lists: 'hole_head' points to a linked
 * list of all the holes (unused memory) in the system; 'free_slots' points to
 * a linked list of table entries that are not in use.  Initially, the former
 * list has one entry for each chunk of physical memory, and the second
 * list links together the remaining table slots.  As memory becomes more
 * fragmented in the course of time (i.e., the initial big holes break up into
 * smaller holes), new table slots are needed to represent them.  These slots
 * are taken from the list headed by 'free_slots'.
 */
  int i, first = 0;
  register struct hole *hp;
  int nodes, largest;

  /* Put all holes on the free list. */
  for (hp = &hole[0]; hp < &hole[_NR_HOLES]; hp++) {
	hp->h_next = hp + 1;
	hp->h_base = hp->h_len = 0;
  }
  hole[_NR_HOLES-1].h_next = NULL;
  hole_head = NULL;
  free_slots = &hole[0];

  addr_init(&addravl);

  total_pages = 0;

  /* Use the chunks of physical memory to allocate holes. */
  for (i=NR_MEMS-1; i>=0; i--) {
  	if (chunks[i].size > 0) {
		phys_bytes from = CLICK2ABS(chunks[i].base),
			to = CLICK2ABS(chunks[i].base+chunks[i].size)-1;
		if(first || from < mem_low) mem_low = from;
		if(first || to > mem_high) mem_high = to;
		free_mem(chunks[i].base, chunks[i].size);
		total_pages += chunks[i].size;
		first = 0;
	}
  }

  CHECKHOLES;
}

#if SANITYCHECKS
PRIVATE void sanitycheck(void)
{
	pagerange_t *p, *prevp = NULL;
	addr_iter iter;
	addr_start_iter_least(&addravl, &iter);
	while((p=addr_get_iter(&iter))) {
		SLABSANE(p);
		assert(p->size > 0);
		if(prevp) {
			assert(prevp->addr < p->addr);
			assert(prevp->addr + p->addr < p->addr);
		}
		addr_incr_iter(&iter);
	}
}
#endif

PUBLIC void memstats(int *nodes, int *pages, int *largest)
{
	pagerange_t *p, *prevp = NULL;
	addr_iter iter;
	addr_start_iter_least(&addravl, &iter);
	*nodes = 0;
	*pages = 0;
	*largest = 0;
#if SANITYCHECKS
	sanitycheck();
#endif
	while((p=addr_get_iter(&iter))) {
		SLABSANE(p);
		(*nodes)++;
		(*pages)+= p->size;
		if(p->size > *largest)
			*largest = p->size;
		addr_incr_iter(&iter);
	}
}

/*===========================================================================*
 *				alloc_pages				     *
 *===========================================================================*/
PRIVATE PUBLIC phys_bytes alloc_pages(int pages, int memflags, phys_bytes *len)
{
	addr_iter iter;
	pagerange_t *pr;
	int incr;
	phys_bytes boundary16 = 16 * 1024 * 1024 / VM_PAGE_SIZE;
	phys_bytes boundary1  =  1 * 1024 * 1024 / VM_PAGE_SIZE;
	phys_bytes mem;
#if SANITYCHECKS
	int firstnodes, firstpages, wantnodes, wantpages;
	int finalnodes, finalpages;
	int largest;

	memstats(&firstnodes, &firstpages, &largest);
	sanitycheck();
	wantnodes = firstnodes;
	wantpages = firstpages - pages;
#endif

	if(memflags & (PAF_LOWER16MB|PAF_LOWER1MB)) {
		addr_start_iter_least(&addravl, &iter);
		incr = 1;
	} else {
		addr_start_iter_greatest(&addravl, &iter);
		incr = 0;
	}

#if NONCONTIGUOUS
	/* If NONCONTIGUOUS is on, allocate physical pages single
	 * pages at a time, accomplished by returning single pages
	 * if the caller can handle that (indicated by PAF_FIRSTBLOCK).
	 */
	if(memflags & PAF_FIRSTBLOCK) {
		assert(!(memflags & PAF_CONTIG));
		pages = 1;
	}
#endif

	while((pr = addr_get_iter(&iter))) {
		SLABSANE(pr);
		assert(pr->size > 0);
		if(pr->size >= pages || (memflags & PAF_FIRSTBLOCK)) {
			if(memflags & PAF_LOWER16MB) {
				if(pr->addr + pages > boundary16)
					return NO_MEM;
			}

			if(memflags & PAF_LOWER1MB) {
				if(pr->addr + pages > boundary1)
					return NO_MEM;
			}

			/* good block found! */
			break;
		}
		if(incr)
			addr_incr_iter(&iter);
		else
			addr_decr_iter(&iter);
	}

	if(!pr) {
		if(len)
			*len = 0;
#if SANITYCHECKS
		assert(largest < pages);
#endif
	   return NO_MEM;
	}

	SLABSANE(pr);

	if(memflags & PAF_FIRSTBLOCK) {
		assert(len);
		/* block doesn't have to as big as requested;
		 * return its size though.
		 */
		if(pr->size < pages) {
			pages = pr->size;
#if SANITYCHECKS
			wantpages = firstpages - pages;
#endif
		}
	}

	if(len)
		*len = pages;

	/* Allocated chunk is off the end. */
	mem = pr->addr + pr->size - pages;

	assert(pr->size >= pages);
	if(pr->size == pages) {
		pagerange_t *prr;
		prr = addr_remove(&addravl, pr->addr);
		assert(prr);
		assert(prr == pr);
		SLABFREE(pr);
#if SANITYCHECKS
		wantnodes--;
#endif
	} else {
		USE(pr, pr->size -= pages;);
	}

	if(memflags & PAF_CLEAR) {
		int s;
		if ((s= sys_memset(0, CLICK_SIZE*mem,
			VM_PAGE_SIZE*pages)) != OK) 
			panic("alloc_mem: sys_memset failed: %d", s);
	}

#if SANITYCHECKS
	memstats(&finalnodes, &finalpages, &largest);
	sanitycheck();

	assert(finalnodes == wantnodes);
	assert(finalpages == wantpages);
#endif

	return mem;
}

/*===========================================================================*
 *				free_pages				     *
 *===========================================================================*/
PRIVATE void free_pages(phys_bytes pageno, int npages)
{
	pagerange_t *pr, *p;
	addr_iter iter;
#if SANITYCHECKS
	int firstnodes, firstpages, wantnodes, wantpages;
	int finalnodes, finalpages, largest;

	memstats(&firstnodes, &firstpages, &largest);
	sanitycheck();

	wantnodes = firstnodes;
	wantpages = firstpages + npages;
#endif

	assert(!addr_search(&addravl, pageno, AVL_EQUAL));

#if JUNKFREE
       if(sys_memset(0xa5a5a5a5, VM_PAGE_SIZE * pageno,
               VM_PAGE_SIZE * npages) != OK)
                       panic("free_pages: sys_memset failed");
#endif

	/* try to merge with higher neighbour */
	if((pr=addr_search(&addravl, pageno+npages, AVL_EQUAL))) {
		USE(pr, pr->addr -= npages;
			pr->size += npages;);
	} else {
		if(!SLABALLOC(pr))
			panic("alloc_pages: can't alloc");
#if SANITYCHECKS
		memstats(&firstnodes, &firstpages, &largest);

		wantnodes = firstnodes;
		wantpages = firstpages + npages;

		sanitycheck();
#endif
		assert(npages > 0);
		USE(pr, pr->addr = pageno;
			 pr->size = npages;);
		addr_insert(&addravl, pr);
#if SANITYCHECKS
		wantnodes++;
#endif
	}

	addr_start_iter(&addravl, &iter, pr->addr, AVL_EQUAL);
	p = addr_get_iter(&iter);
	assert(p);
	assert(p == pr);

	addr_decr_iter(&iter);
	if((p = addr_get_iter(&iter))) {
		SLABSANE(p);
		if(p->addr + p->size == pr->addr) {
			USE(p, p->size += pr->size;);
			addr_remove(&addravl, pr->addr);
			SLABFREE(pr);
#if SANITYCHECKS
			wantnodes--;
#endif
		}
	}


#if SANITYCHECKS
	memstats(&finalnodes, &finalpages,  &largest);
	sanitycheck();

	assert(finalnodes == wantnodes);
	assert(finalpages == wantpages);
#endif
}

#define NR_DMA	16

PRIVATE struct dmatab
{
	int dt_flags;
	endpoint_t dt_proc;
	phys_bytes dt_base;
	phys_bytes dt_size;
	phys_clicks dt_seg_base;
	phys_clicks dt_seg_size;
} dmatab[NR_DMA];

#define DTF_INUSE	1
#define DTF_RELEASE_DMA	2
#define DTF_RELEASE_SEG	4

/*===========================================================================*
 *				do_adddma				     *
 *===========================================================================*/
PUBLIC int do_adddma(message *msg)
{
	endpoint_t req_proc_e, target_proc_e;
	int i, proc_n;
	phys_bytes base, size;
	struct vmproc *vmp;

	req_proc_e= msg->VMAD_REQ;
	target_proc_e= msg->VMAD_EP;
	base= msg->VMAD_START;
	size= msg->VMAD_SIZE;

	/* Find empty slot */
	for (i= 0; i<NR_DMA; i++)
	{
		if (!(dmatab[i].dt_flags & DTF_INUSE))
			break;
	}
	if (i >= NR_DMA)
	{
		printf("vm:do_adddma: dma table full\n");
		for (i= 0; i<NR_DMA; i++)
		{
			printf("%d: flags 0x%x proc %d base 0x%lx size 0x%lx\n",
				i, dmatab[i].dt_flags,
				dmatab[i].dt_proc,
				dmatab[i].dt_base,
				dmatab[i].dt_size);
		}
		panic("adddma: table full");
		return ENOSPC;
	}

	/* Find target process */
	if (vm_isokendpt(target_proc_e, &proc_n) != OK)
	{
		printf("vm:do_adddma: endpoint %d not found\n", target_proc_e);
		return EINVAL;
	}
	vmp= &vmproc[proc_n];
	vmp->vm_flags |= VMF_HAS_DMA;

	dmatab[i].dt_flags= DTF_INUSE;
	dmatab[i].dt_proc= target_proc_e;
	dmatab[i].dt_base= base;
	dmatab[i].dt_size= size;

	return OK;
}

/*===========================================================================*
 *				do_deldma				     *
 *===========================================================================*/
PUBLIC int do_deldma(message *msg)
{
	endpoint_t req_proc_e, target_proc_e;
	int i, j, proc_n;
	phys_bytes base, size;
	struct vmproc *vmp;

	req_proc_e= msg->VMDD_REQ;
	target_proc_e= msg->VMDD_EP;
	base= msg->VMDD_START;
	size= msg->VMDD_SIZE;

	/* Find slot */
	for (i= 0; i<NR_DMA; i++)
	{
		if (!(dmatab[i].dt_flags & DTF_INUSE))
			continue;
		if (dmatab[i].dt_proc == target_proc_e &&
			dmatab[i].dt_base == base &&
			dmatab[i].dt_size == size)
		{
			break;
		}
	}
	if (i >= NR_DMA)
	{
		printf("vm:do_deldma: slot not found\n");
		return ESRCH;
	}

	if (dmatab[i].dt_flags & DTF_RELEASE_SEG)
	{
		/* Check if we have to release the segment */
		for (j= 0; j<NR_DMA; j++)
		{
			if (j == i)
				continue;
			if (!(dmatab[j].dt_flags & DTF_INUSE))
				continue;
			if (!(dmatab[j].dt_flags & DTF_RELEASE_SEG))
				continue;
			if (dmatab[i].dt_proc == target_proc_e)
				break;
		}
		if (j >= NR_DMA)
		{
			/* Last segment */
			free_mem(dmatab[i].dt_seg_base,
				dmatab[i].dt_seg_size);
		}
	}

	dmatab[i].dt_flags &= ~DTF_INUSE;

	return OK;
}

/*===========================================================================*
 *				do_getdma				     *
 *===========================================================================*/
PUBLIC int do_getdma(message *msg)
{
	endpoint_t target_proc_e;
	int i, proc_n;
	phys_bytes base, size;
	struct vmproc *vmp;

	/* Find slot to report */
	for (i= 0; i<NR_DMA; i++)
	{
		if (!(dmatab[i].dt_flags & DTF_INUSE))
			continue;
		if (!(dmatab[i].dt_flags & DTF_RELEASE_DMA))
			continue;

		printf("do_getdma: setting reply to 0x%lx@0x%lx proc %d\n",
			dmatab[i].dt_size, dmatab[i].dt_base,
			dmatab[i].dt_proc);
		msg->VMGD_PROCP= dmatab[i].dt_proc;
		msg->VMGD_BASEP= dmatab[i].dt_base;
		msg->VMGD_SIZEP= dmatab[i].dt_size;

		return OK;
	}

	/* Nothing */
	return EAGAIN;
}



/*===========================================================================*
 *				release_dma				     *
 *===========================================================================*/
PUBLIC void release_dma(struct vmproc *vmp)
{
	int i, found_one;

	panic("release_dma not done");
#if 0

	found_one= FALSE;
	for (i= 0; i<NR_DMA; i++)
	{
		if (!(dmatab[i].dt_flags & DTF_INUSE))
			continue;
		if (dmatab[i].dt_proc != vmp->vm_endpoint)
			continue;
		dmatab[i].dt_flags |= DTF_RELEASE_DMA | DTF_RELEASE_SEG;
		dmatab[i].dt_seg_base= base;
		dmatab[i].dt_seg_size= size;
		found_one= TRUE;
	}

	if (!found_one)
		free_mem(base, size);

	msg->VMRD_FOUND = found_one;
#endif

	return;
}

/*===========================================================================*
 *				printmemstats				     *
 *===========================================================================*/
void printmemstats(void)
{
	int nodes, pages, largest;
        memstats(&nodes, &pages, &largest);
        printf("%d blocks, %d pages (%lukB) free, largest %d pages (%lukB)\n",
                nodes, pages, (u32_t) pages * (VM_PAGE_SIZE/1024),
		largest, (u32_t) largest * (VM_PAGE_SIZE/1024));
}


#if SANITYCHECKS

/*===========================================================================*
 *				usedpages_reset				     *
 *===========================================================================*/
void usedpages_reset(void)
{
	memset(pagemap, 0, sizeof(pagemap));
}

/*===========================================================================*
 *				usedpages_add				     *
 *===========================================================================*/
int usedpages_add_f(phys_bytes addr, phys_bytes len, char *file, int line)
{
	pagerange_t *pr;
	u32_t pagestart, pages;

	if(!incheck)
		return OK;

	assert(!(addr % VM_PAGE_SIZE));
	assert(!(len % VM_PAGE_SIZE));
	assert(len > 0);
	assert_range(addr, len);

	pagestart = addr / VM_PAGE_SIZE;
	pages = len / VM_PAGE_SIZE;

	while(pages > 0) {
		phys_bytes thisaddr;
		assert(pagestart > 0);
		assert(pagestart < MAXPAGES);
		thisaddr = pagestart * VM_PAGE_SIZE;
		if(GET_BIT(pagemap, pagestart)) {
			int i;
			printf("%s:%d: usedpages_add: addr 0x%lx reused.\n",
				file, line, thisaddr);
			return EFAULT;
		}
		SET_BIT(pagemap, pagestart);
		pages--;
		pagestart++;
	}

	return OK;
}

#endif

/*===========================================================================*
 *				alloc_mem_in_list			     *
 *===========================================================================*/
struct memlist *alloc_mem_in_list(phys_bytes bytes, u32_t flags)
{
	phys_bytes rempages;
	struct memlist *head = NULL, *tail = NULL;

	assert(bytes > 0);
	assert(!(bytes % VM_PAGE_SIZE));

	rempages = bytes / VM_PAGE_SIZE;

	/* unless we are told to allocate all memory
	 * contiguously, tell alloc function to grab whatever
	 * block it can find.
	 */
	if(!(flags & PAF_CONTIG))
		flags |= PAF_FIRSTBLOCK;

	do {
		struct memlist *ml;
		phys_bytes mem, gotpages;
		vir_bytes freed = 0;

		do {
			mem = alloc_pages(rempages, flags, &gotpages);

			if(mem == NO_MEM) {
				freed = free_yielded(rempages * VM_PAGE_SIZE);
			}
		} while(mem == NO_MEM && freed > 0);

		if(mem == NO_MEM) {
			printf("alloc_mem_in_list: giving up, %lukB missing\n",
				rempages * VM_PAGE_SIZE/1024);
			printmemstats();
			free_mem_list(head, 1);
			return NULL;
		}

		assert(gotpages <= rempages);
		assert(gotpages > 0);

		if(!(SLABALLOC(ml))) {
			free_mem_list(head, 1);
			free_pages(mem, gotpages);
			return NULL;
		}

		USE(ml,
			ml->phys = CLICK2ABS(mem);
			ml->length = CLICK2ABS(gotpages);
			ml->next = NULL;);
		if(tail)
			tail->next = ml;
		tail = ml;
		if(!head)
			head = ml;
		rempages -= gotpages;
	} while(rempages > 0);

    {
	struct memlist *ml;
	for(ml = head; ml; ml = ml->next) {
		assert(ml->phys);
		assert(ml->length);
#if NONCONTIGUOUS
		if(!(flags & PAF_CONTIG)) {
			assert(ml->length == VM_PAGE_SIZE);
			if(ml->next)
				assert(ml->phys + ml->length != ml->next->phys);
		}
#endif
	}
    }

	return head;
}

/*===========================================================================*
 *				free_mem_list			     	     *
 *===========================================================================*/
void free_mem_list(struct memlist *list, int all)
{
	while(list) {
		struct memlist *next;
		next = list->next;
		assert(!(list->phys % VM_PAGE_SIZE));
		assert(!(list->length % VM_PAGE_SIZE));
		if(all)
			free_pages(list->phys / VM_PAGE_SIZE,
			list->length / VM_PAGE_SIZE);
		SLABFREE(list);
		list = next;
	}
}

/*===========================================================================*
 *				print_mem_list			     	     *
 *===========================================================================*/
void print_mem_list(struct memlist *list)
{
	while(list) {
		assert(list->length > 0);
		printf("0x%lx-0x%lx", list->phys, list->phys+list->length-1);
		printf(" ");
		list = list->next;
	}
	printf("\n");
}

