#define SCCSID	"@(#)vmkrh.c    6.10 92/03/11"

/*
 *	 IBM/Microsoft Confidential
 *
 *	 Copyright (c) IBM Corporation 1987, 1989
 *	 Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	 All Rights Reserved
 *
 *       History of Changes:
 * 147968 5/11/96  RJM Change FSD and DD system owners to use HMTEs
 */

#define INCL_DOSERRORS
#define INCL_OS2STD
#include <os2.h>

#define INCL_DOSP
#include <os2p.h>


#define FNO_NUM  FNO_VMKRH_C
#include <fnomem.h>
#include <osmac.h>

#include <vmowner.h>
#include <mi.h>
#include <pgexport.h>
#include <vmexport.h>
#include <vmlocal.h>
#include <tkexport.h>
#include <vmkh.h>	     // must be after tkexport.h for KSEM definition
/*
 * BUGBUG IDB 04/18/90 -Need pglocal.h for PGIsAbove16Meg macro and pgPhysPages
 */
#include <pglocal.h>
#include <string.h>
#include <selxport.h>
#include <bseerr.h>   // defect #98488
#include                <dossymc.h>         /* 147968 tkptda.h needs it */
#include                <sigdef.h>          /* 147968 tkptda.h needs it */
#include                <except.h>          /* 147968 tkptda.h needs it */
#include                <infoseg.h>         /* 147968 tkptda.h needs it */
#include                <curdir.h>          /* 147968 tkptda.h needs it */
#include                <dirent.h>          /* 147968 tkptda.h needs it */
#include                <find.h>            /* 147968 tkptda.h needs it */
#include                <screen.h>          /* 147968 tkptda.h needs it */
#include                <sem.h>             /* 147968 tkptda.h needs it */
#include                <tkptda.h>          /* 147968 */
#define FOR_EXEHDR      1                   /* 147968 */
#include <exe386.h>                         /* 147968 */
#include <ldrxport.h>                       /* 147968 */
#include <ldrvars.h>                        /* 147968 */

/***LD+ Global Resident Heap Data */

ulong_t 	ackrhsInit[krh_CFREELIST] = {
			   krhs_cbMax0, /* Max blocksize for free section 0 */
			   krhs_cbMax1, /* Max blocksize for free section 1 */
			   krhs_cbMax2, /* Max blocksize for free section 2 */
			   krhs_cbMax3, /* Max blocksize for free section 3 */
			   krhs_cbMax4, /* Max blocksize for free section 4 */
};

VMKHBA		khbaFree = {(SEL) NA, VMKRHFOWNER};
VMKHBA		*pkhbaFree = &khbaFree;
VMKHBA		khbaLast = {(SEL) NA, VMKRHLOWNER};
VMKHBA		*pkhbaLast = &khbaLast;
VMKHBA		khbaBusy = {(SEL) NA, VMKRHBOWNER};
VMKHBA		*pkhbaBusy = &khbaBusy;
VMKHBA		khbaDBusy = {(SEL) NA, VMKRHBDONOWNER};
VMKHBA		*pkhbaDBusy = &khbaDBusy;

/*end*/

/***LD+ Global Swappable Heap Data */

VMKSHD		*pkshdVMDescs;		/* Pointer to array of BMP32 managed
/*end*/

extern KSEMMTX pgCommitSem;		/* 77961 */

/***EP	VMAllocKHB - Allocate from a Kernel Resident or Swappable Heap
 *
 *	This function allocates a block from a Kernel Resident or Swappable
 *	Heap.  Certain properties of the block are specifiable via flags.
 *	Allocations from a resident heap requiring low physical memory
 *	(addresses < 1Meg) must use the public low memory heaps.
 *
 *	int VMAllocKHB(VMHKH hkh, ulong_t cb, VMHOB hobOwner, ulong_t fl,
 *	  VMKHBI *pkhbi)
 *
 *	ENTRY		hkh		- Kernel heap header handle
 *					  created by VMCreateKH.  Special
 *					  values map the public heaps:
 *					  - VM_HKH_PUB_RESRW
 *					    Public Resident RW Heap
 *					  - VM_HKH_PUB_RESRO
 *					    Public Resident RO Heap
 *					  - VM_HKH_PUB_RES1MRW
 *					    Public Resident Lowmem RW Heap
 *					  - VM_HKH_PUB_RES1MRO
 *					    Public Resident Lowmem RO Heap
 *					  - VM_HKH_PUB_SWAPRW
 *					    Public Swappable RW Heap
 *					  - VM_HKH_PUB_SWAPRO
 *			cb		- Size of block requested.  The actual
 *					  size requested will be rounded up to
 *					  dword granularity.
 *			hobOwner	- Owner of heap block
 *			fl		- Optional Flags specific to this
 *					  request
 *					  - VMAC_ALIGNSEL
 *					      64K alignment needed
 *					  - VMAC_ALIGNPAGE
 *					      Page alignment needed
 *					  - VMAC_ALIGNPARA
 *					      Para alignment needed
 *					  - VMAC_NOXPAGE
 *					      Block not to cross page boundary
 *					      (swappable heap allocations only,
 *					       header can start on preceding
 *					       page).
 *					  - PG_U
 *					      Accessible from user privilege
 *					      (needed by sysinit and device
 *					       driver ring 3 init code)
 *					  - PG_CONTIG
 *					      Contiguous pages needed
 *					      (resident allocations only)
 *					  - PG_16M
 *					      Physical memory below 16Meg
 *					      (resident allocations only)
 *			pkhbi		 - Pointer to heap block info
 *					    Address of block only is
 *					    always returned.  Selector is
 *					    input and left unchanged for
 *					    selector-mapped blocks.
 *					    hobOwner is filed in
 *					  NOTE:  If not allocating a
 *					    selector mapped block, a
 *					    pointer pointer of any type
 *					    may be passed for this param.
 *
 *	RETURN		int		- Result code (success = NO_ERROR)
 */

int VMAllocKHB(hkh, cb, hobOwner, fl, pkhbi)
VMHKH			hkh;		/* Heap header handle */
ulong_t 		cb;		/* Size of request in bytes */
VMHOB			hobOwner;	/* Owner of block */
ulong_t 		fl;		/* Flags specific to this request */
VMKHBI			*pkhbi; 	/* Heap block info */
{
  register char 	*pb;		/* Block pointer */
  char			*pbData;	/* Pointer to block's data */
  char			*pbResid;	/* Residual free block pointer */
  char			*pbYield;	/* Pointer to yield block */
  char			*pbDecommitMin; /* Decommitted region in free block */
  char			*pbDecommitMax; /* End of decommitted region in fb */
  char			*pbAlloc;	/* Pointer to allocated block */
  char			*pbAllocLimit;	/* Allocation limit */
  char			*pbOldHead;	/* Pointer to old free list sect head */
  char			*pbNewHead;	/* Pointer to new free list sect head */
  VMKRH 		*pkrh;		/* Resident Heap Pointer */
  VMKRHS		*pkrhs; 	/* Free list header pointer */
  VMKHBA		*pkhbaAlloc;	/* Pointer to attribute area */
  register ulong_t	cbRound;	/* Rounded request size */
  ulong_t		cbResid;	/* Size of residual block */
  ulong_t		cbAvail;	/* Size of block available */
  ulong_t		cbBefore;	/* Size of block bef pg bdry */
  ulong_t		cbHeader;	/* Size of header incl. prefix block */
  ulong_t		flPhys; 	/* Page Manager Flags */
  ulong_t		flCarve;	/* Flags for block carving */
  ulong_t		cbAlign;	/* Allowance for alignment */
  int			cLoops; 	/* Count of loop iterations */
  int			rc;		/* Return code */
  VMKHBA		khbaAlloc;	/* Attribute area for allocated block */

  cbAlign = 0;

  /*
   *  Verify that heap header handle is valid.
   */

  vmAssert(vmIsHKH(hkh));

  /*
   *  If swappable request, call swappable heap allocation function.
   */

  if(vmIsHKSH(hkh))
    {
      return(vmAllocKSHB(hkh, cb, fl, hobOwner, pkhbi));
    }

  /*
   *  Validate parameters.  Check that only permitted flags are given,
   *  the request size is non-zero, the specified owner is valid and verify
   *  that the specified heap handle is that of a resident heap.  Map the
   *  handle to a heap header pointer.
   */
// perf - mem reduction : for retail and hstrict builds combine
//			  RES1MRO and RES1MRW and RESRO with RESRW
#ifndef VMSTRICT
   if (hkh == VM_HKH_PUB_RES1MRO || hkh == VM_HKH_PUB_RES1MRW)	{
       fl |= PG_1M;
   }
#endif

  vmAssert(fl == (fl & VMAC_KHBMASK));
  vmAssert(vmIsValidOwnerKRHB(hobOwner));
  vmAssert(cb != 0);
  vmAssert(vmIsHKRH(hkh));
  pkrh = apkh[hkh].kh_pkrh;
  vmAssert(vmIsValidKRH(pkrh));
  pbAlloc = (char *) NULL;

  cbRound = vmRoundReqKRHB(cb, fl);

  /*
   *  Case (1) - Fast path for generic allocations.
   *
   *  If the following conditions are satisfied, a quick allocation is done.
   *
   *  - The allocation is from a resident writeable heap
   *  - No selector mapping is requested
   *  - No page or paragraph alignment is requested
   *  - No physical memory address restriction (< 1Meg or < 16Meg addresses)
   *  - The request size does not require a block that can have decommitted
   *	pages.
   */

  if(cb <= PAGESIZE + sizeof(VMKRHF) - sizeof(VMKRHB))
    {
      if((fl & (VMAC_SELALLOC|VMAC_ALIGNMASK|PG_U|PG_16M|PG_CONTIG)) == 0)
	{

	  /*
	   *  Search free list from start of first eligible section up to
	   *  end of blocks that do not exceed PAGESIZE + sizeof(VMKRHF),
	   *  this being the largest size of block that is guaranteed not
	   *  to contain any decommitted pages.  For speed, the optimized
	   *  scan does not take the heap semaphore and therefore it must
	   *  not yield or block.  A yield is not currently needed as the
	   *  scan is short.
	   */

	  TKBeginNoBlock();

	  pkrhs = vmFirstPKRHS(pkrh, cbRound);
	  pbAlloc = vmHeadFreeKRHB(pkrhs);

	  while(vmIsRegKRHB(pbAlloc) && !vmIsDummyKRHB(pbAlloc))
	    {
	      if(vmHasYieldedKRHB(pbAlloc))
		{
		  pbAlloc = vmNextFreeKRHB(pbAlloc);
		  continue;
		}

	      if(cbRound <= (cbAvail = vmSizeRegKRHB(pbAlloc)))
		{
		  if(cbAvail > PAGESIZE + sizeof(VMKRHF))
		    {
		      break;
		    }

		  /*
		   *  We can optimize performance for the case where we
		   *  are nibbling a little away from a free block.  If there
		   *  will be a residual free block and it will belong to the
		   *  same free list section as the original free block we
		   *  can avoid having to remove the original block from its
		   *  free list section only to insert the residual block in
		   *  the same section.  In this situation, we carve out the
		   *  allocation request from the end of the free block and
		   *  simply reduce the size of the original free block.  No
		   *  need to update the free lists since the residual free
		   *  block has the same address as the original free block.
		   */

		  pkrhs = vmFirstPKRHS(pkrh, cbAvail);
		  if((cbResid = cbAvail-cbRound) > (pkrhs-1)->krhs_cbMax)
		    {
		      /*
		       *  Residual free block belongs to same free list
		       *  section as original free block and there are no
		       *  decommitted pages in original free block.  Carve
		       *  allocation from end of original free block.
		       */

		      pbResid = pbAlloc;
		      pbAlloc += cbResid;
		      *((ulong_t *) pbResid) -= cbRound;
		      *((ulong_t *) pbAlloc) =
			vmMakeHdrRegKRHB(hobOwner, cbRound, krhb_PFREE);

		      /*
		       *  Since we are carving our allocation from the end
		       *  of a free block, we need to clear the "preceding
		       *  block is free" bit in the succeeding block's header.
		       */

		      *((ulong_t *)(pbAlloc + cbRound)) &=
			((vmIsRegKRHB(pbAlloc + cbRound)) ?
			 ~krhb_PFREE : ~krhb_ATTR_PFREE);

		      /*
		       *  Since the newly allocated block is preceded by the
		       *  residual free block, we need to set the "preceding
		       *  block is free" bit in the allocated block (already
		       *  done) and store a back pointer to the residual free
		       *  block in its last ulong_t.  Set krhb_PBBACK bit to
		       *  indicate that this location does contain a back
		       *  pointer.  We do not set the bit or back pointer
		       *  for residual blocks of minimum size since there is
		       *  a conflict with the free list back pointer.
		       */

		      if(cbResid > krhb_REG_MIN_SIZE)
			{
			  *((ulong_t *)(pbAlloc - sizeof(ulong_t)))
			    = ((ulong_t) pbResid) | krhb_PBBACK;
			}

		      /*
		       *  Return address of allocated block's data to caller.
		       */

		      pkhbi->khbi_pbData = pbAlloc + sizeof(VMKRHB);
		      TKEndNoBlock();
		      return(NO_ERROR);
		    }

		  /*
		   *  There is either not enough room for a residual free
		   *  block, or that block will belong to a different free
		   *  list section.  Remove the original free block from
		   *  the free list and update free list section head
		   *  pointers that reference the block.
		   */

		  vmPrevFreeKRHB(vmNextFreeKRHB(pbAlloc)) =
		    vmPrevFreeKRHB(pbAlloc);
		  vmNextFreeKRHB(vmPrevFreeKRHB(pbAlloc)) =
		    vmNextFreeKRHB(pbAlloc);
		  pbNewHead = vmNextFreeKRHB(pbAlloc);
		  /* pkrhs = vmFirstPKRHS(pkrh, cbAvail); */

		  while(vmHeadFreeKRHB(pkrhs) == pbAlloc)
		    {
		      vmHeadFreeKRHB(pkrhs) = pbNewHead;
		      pkrhs--;
		    }

		  /*
		   *  If there is enough room for a residual free block,
		   *  initialize its header and add it to the head of the
		   *  appropriate free list section.  Update free list section
		   *  heads.
		   */

		  if((cbAvail - cbRound) >= sizeof(VMKRHF))
		    {
		      /*
		       *  Set residual free block header.
		       */

		      pbResid = pbAlloc + cbRound;
		      *((ulong_t *) pbResid) =
		       (((ulong_t) VMKRHFOWNER) << 16) | cbResid;

		      /*
		       *  If block size > the minimum size, store
		       *  back pointer to it in the last ulong.  Set
		       *  indicator (bit 1) that back pointer exists.
		       */

		      if(cbResid > sizeof(VMKRHF))
			{
			  *((ulong_t *)(pbResid + cbResid - sizeof(ulong_t)))
			    = ((ulong_t) pbResid) | krhb_PBBACK;
			}

		      /*
		       *  Add residual free block to the head of the
		       *  appropriate free list section.
		       */

		      pkrhs = vmFirstPKRHS(pkrh, cbResid);
		      pbOldHead = vmHeadFreeKRHB(pkrhs);
		      vmNextFreeKRHB(vmPrevFreeKRHB(pbOldHead)) = pbResid;
		      vmPrevFreeKRHB(pbResid) = vmPrevFreeKRHB(pbOldHead);
		      vmPrevFreeKRHB(pbOldHead) = pbResid;
		      vmNextFreeKRHB(pbResid) = pbOldHead;

		      /*
		       *  Update free list section head pointers that
		       *  referenced the previous free list head to make
		       *  them point to the residual free block.
		       */

		      while(vmHeadFreeKRHB(pkrhs) == pbOldHead)
			{
			  vmHeadFreeKRHB(pkrhs) = pbResid;
			  pkrhs--;
			}
		    }

		  /*
		   *  Else, not enough room for a residual block.  Specify
		   *  that the whole block will be allocated.  Clear the
		   *  Preceding block is free bit in the succeeding block.
		   */

		  else
		    {
		      cbRound = cbAvail;
		      *((ulong_t *)(pbAlloc + cbRound)) &=
			((vmIsRegKRHB(pbAlloc + cbRound)) ?
			~krhb_PFREE : ~krhb_ATTR_PFREE);
		    }

		  /*
		   *  Allocate a block of the rounded requested size, or
		   *  the entire free block if not room for a residual block.
		   */

		  *((ulong_t *) pbAlloc) =
		     vmMakeHdrRegKRHB(hobOwner,
				      cbRound,
				      (*((ulong_t *) pbAlloc)) &
				      (krhb_YIELD | krhb_PFREE));

		  /*
		   *  Return the address of the block's data
		   *  to the caller.
		   */

		  pkhbi->khbi_pbData = pbAlloc + sizeof(VMKRHB);
		  vmAssert(vmIsValidKRH(pkrh));
		  TKEndNoBlock();
		  return(NO_ERROR);
		}

	      /*
	       *  Block was too small, try next block.
	       */

	      pbAlloc = vmNextFreeKRHB(pbAlloc);
	    } /* end while */

	  TKEndNoBlock();
	}
    }
    if (fl & PG_NOBLOCK) return(ERROR_BLOCKED); // defect 98488

  /*
   *  (2)  Case 2 - General case
   *
   *  The request was not eligible for an optimized search of the regular
   *  part of the free list, or the search was unsuccessful.  Search remainder
   *  of the heap.  First, verify that request size does not exceed size
   *  of heap.	Obtain rounded request size and set up attributes for block
   *  to be allocated.
   */

  if(cb > pkrh->krh_pbEndRes-pkrh->krh_pbFirst-sizeof(VMKRHB)-sizeof(VMKHBA))
    {
      return(ERROR_NOT_ENOUGH_MEMORY);
    }
//cbRound = vmRoundReqKRHB(cb, fl);
  pkhbaAlloc = SSToDS(&khbaAlloc);
  pkhbaAlloc->khba_hobOwner = hobOwner;
  pkhbaAlloc->khba_sel = (SEL) NA;
  if(fl & VMAC_SELALLOC)
    {
      pkhbaAlloc->khba_sel = pkhbi->khbi_sel;
    }
  cbHeader = sizeof(VMKRHB);

  /*
   *  Obtain alignment mask.
   */

  switch(fl & VMAC_ALIGNMASK)
    {
      case VMAC_ALIGNPAGE:
	cbAlign = 0xfffL;
	break;

      case VMAC_ALIGNPARA:
	cbAlign = 0xfL;
	break;

      case VMAC_ALIGNSEL:
	cbAlign = 0xffffL;
	break;

      default:
	cbAlign = krhb_GRAN_MASK;
    }

  /*
   *  Set carving flags.  If a selector-mapped block, type must be
   *  attributed regardless of size.  If a contiguous allocation > 1/2 page,
   *  limit carving to decommitted interiors of free blocks.  Isolate flags
   *  passed in that decribe physical memory properties
   */

  flCarve = (fl & VMAC_SELALLOC) ? krhb_ATTR : NA;

// perf - mem reduction : for retail and hstrict builds combine
//			  RES1MRO and RES1MRW and RESRO with RESRW
#ifdef VMSTRICT
  flPhys = (fl & (PG_U | PG_CONTIG | PG_16M));
#else
  flPhys = (fl & (PG_U | PG_CONTIG | PG_16M | PG_1M));
#endif

  if((flPhys & PG_CONTIG) && (cbAlign > 0xfL || cbRound > krhb_SMALL)) //b721296
    {
      flCarve |= krhb_CR_USEDECO;
    }

  /*
   *  For loop to control the allocation.  On each iteration, scan the relevant
   *  part of the free list (if any) once (see inner for loop).  If a suitable
   *  block is found, allocate from it and return.  If no block can be found,
   *  try to take the heap semaphore without having to block.  If this cannot
   *  be done, wait for the next clear of the semaphore and then do another
   *  iteration (a new block may have become available in the interim).
   *  Repeat until either a block is found or the semaphore is taken without
   *  blocking.  If the semaphore is successfully taken without blocking, exit
   *  from do loop to attempt heap growth.
   */

scanheap:

  pkrhs = vmFirstPKRHS(pkrh, cbRound);
  pb = (pbAlloc == (char *) NULL) ? vmHeadFreeKRHB(pkrhs) : pbAlloc;

  /*
   *  Initialize loop count and yield block pointer.
   */

  cLoops = 0;
  pbYield = NULL;

  /*
   *  Scan the free list.  If we have already scanned part of the
   *  free list via the preceding optimized scan, we continue from
   *  where we left off.  The block pointer pb is therefore already
   *  initialized.
   */

  for(;(!vmIsDummyKRHB(pb)); vmYieldCheckKRHB(pkrh, pb, cLoops),
		     pb = vmNextFreeKRHB(pb))
    {
      /*
       *  Verify that the pointer to the next block on the free list
       *  lies within the range of pointers to this heap, and check
       *  that the block is valid.
       */

      vmAssert(vmIsWithinKRH(pkrh, pb));
      vmAssert(vmHasValidOwnerKRHB(pb));
      vmAssert(vmIsFreeKRHB(pb));

      /* KDH - Check to see if the yield bit of this free block is set.
       *       If it is, that means that somebody has yielded the CPU
       *       while looking at this block, and we should skip it;
       *       otherwise, whoever has yielded the CPU while looking
       *       at this block would not be able to resume the search.
       *       (defect 150535)
       */

      if (vmHasYieldedKRHB(pb))
	 {
	   continue;
	 }

      /*
       *  Check if block is large enough (before alignment or
       *  contiguity considered).  Skip if not.
       */

      if((cbAvail = vmSizeKRHB(pb)) < cbRound)
	{
	  continue;
	}

      /*
       *  Set default allocation limits as whole of block.  Set default size
       *  of free block preceding allocation to 0.
       */

      pbAlloc = pb;
      pbAllocLimit = pb + cbAvail;
      cbBefore = 0;

      /*
       *  If special physical memory or page alignment needed, check that a
       *  suitably aligned sub-block can be carved such that either
       *  there is no unused part at the beginning of the block, or
       *  the unused part is large enough to be made into a free block.
       */

      if(flPhys || cbAlign > krhb_GRAN_MASK)
	{
	  /*
	   *  Handle physically contiguous allocation requests.
	   */

	  if(!(flCarve & krhb_CR_USEDECO))
	    {
	      /*
	       *  If small contig or PG_16M request and no alignment, attempt to
	       *  allocate from the committed portions at the beginning
	       *  or end of the block.	If allocation is not possible without
	       *  crossing a page boundary, reject the block.  If we reach
	       *  the end of the free list, break out of for loop to do
	       *  another scan of the free list, examining only decommitted
	       *  regions of blocks.
	       */

	      if(flPhys & (PG_CONTIG | PG_16M))
		{
		  /*
		   *  Set max upper bound for allocation equal to
		   *  end of committed portion clamped to end of block.
		   *  Allow for residual free block header and free list
		   *  links to be carved out of committed portion if the
		   *  block has a decommitted region.
		   */

		  for(;;)
		    {
		      /*
		       *  Examine committed portion at beginning of
		       *  block..  Set limit = start of decommitted
		       *  region minus room for residual free block
		       *  header and links.
		       */

		      pbAlloc = pb;
		      pbAllocLimit = vmDecommitMinKRHB(pb);
		      if(pbAllocLimit > pb + cbAvail)
			{
			  pbAllocLimit = pb + cbAvail;
			}
		      else if(pbAllocLimit < pb + cbAvail)
			{
			  pbAllocLimit -= sizeof(VMKRHF);
			}

		      /*
		       *  If there is room to make the allocation, try 2
		       *  possibilities.  Align the allocation first
		       *  to the beginning of the committed region then to
		       *  the end of the region.
		       */

		      if(pbAlloc + cbRound <= pbAllocLimit)
			{
			  /*
			   *  [1]  Try region aligned at beginning.  If
			   *  a page boundary would not be crossed (contig
			   *  allocs or PG_16M allocs) or the phys memory
			   *  is at addresses < 16M (PG_16M specified) accept
			   *  block by breaking out of inner for loop.
			   */

			  if(vmPhysCheckKRHB(fl,
					     pbAlloc + sizeof(VMKRHB),
					     cbRound - sizeof(VMKRHB)))
			    {
			      break;
			    }

			  /*
			   *  [2]  Try region aligned at end.
			   */

			  pbAlloc = pbAllocLimit - cbRound;
			  if(vmPhysCheckKRHB(fl,
					     pbAlloc + sizeof(VMKRHB),
					     cbRound - sizeof(VMKRHB)))
			    {
			      break;
			    }
			}

		      /*
		       *  Examine committed portion at end of free block.
		       *  Since this is aligned to a page boundary, examine
		       *  only the possibility of allocating a region near
		       *  the beginning of this portion.  Must allow room
		       *  for preceding free block back pointer and attribute
		       *  area.
		       */

		      pbAlloc = vmDecommitMaxKRHB(pb, cbAvail);
		      pbAllocLimit = pb + cbAvail;
		      if(vmDecommitMinKRHB(pb) < pbAlloc)
			{
			  /*
			   *  Clamp decommit max to not be below start of
			   *  block.
			   */

			  if(pbAlloc < pb)
			    {
			      pbAlloc = pb;
			    }

			  /*
			   *  If there will be a preceding free block,
			   *  allow room for its back pointer and attribute
			   *  area.  These must reside beyond the decommitted
			   *  region (if any) that will be mapped out by the
			   *  block.
			   */

			  else
			    {
			      pbAlloc += sizeof(VMKHBA) + sizeof(ulong_t *);
			    }

			  /*
			   *  Check that allocated block's data would not
			   *  cross a page boundary/occupies phys mem < 16Meg
			   *  as required.
			   */

			  if((pbAlloc + cbRound <= pbAllocLimit) &&
			     vmPhysCheckKRHB(fl,
					     pbAlloc + sizeof(VMKRHB),
					     cbRound))
			    {
			      break;
			    }
			}

		      /*
		       *  Block not acceptable for allocation of small
		       *  contiguous or PG_16M	request from either of its
		       *  committed portions.  If there are more free blocks,
		       *  try next one.
		       */

		      if(!vmIsDummyKRHB(pb = vmNextFreeKRHB(pb)))
			{
			  cbAvail = vmSizeKRHB(pb);
			  continue;
			}

		      /*
		       *  No more free blocks.	Repeat partial scan
		       *  of free list looking for a block containing
		       *  a decommitted region.  We start the scan
		       *  at the first free list section having
		       *  blocks large enough to have decommitted
		       *  regions.  Set the size of small contiguous
		       *  allocations to 0 to force allocation to
		       *  take place from within a decommitted
		       *  region.
		       */

		      flCarve |= krhb_CR_USEDECO;
		      pkrhs = vmFirstPKRHS(pkrh,
			PAGESIZE+sizeof(VMKRHF)+ sizeof(VMKRHB));
		      pb = vmPrevFreeKRHB(vmHeadFreeKRHB(pkrhs));
		      pbAlloc = NA;
		      break;
		    }

		  /*
		   *  If allocation pointer is null, block was rejected.
		   *  continue with next block.
		   */

		  if(pbAlloc == NA)
		    {
		      continue;
		    }
		} /* End if small contiguous allocation */
	    } /* End if not restricted to using decommitted memory */

	  /*
	   *  Else, this allocation must be limited to decommitted memory.
	   */

	  else
	    {
	      pbAlloc = vmDecommitMinKRHB(pb) - sizeof(VMKRHB);
	      pbAllocLimit =  vmDecommitMaxKRHB(pb, cbAvail);
	    }

	  /*
	   *  If special alignment needed, align the pointer to the
	   *  appropriate boundary minus size of heap block header.
	   */

	  if(cbAlign > krhb_GRAN_MASK)
	    {
	      pbAlloc = (char *)
		(((ulong_t)(pbAlloc + sizeof(VMKRHB) + cbAlign)) &
		      ~cbAlign) - sizeof(VMKRHB);
	    }

	  /*
	   *  Compute size of portion of free block (if any) preceding the
	   *  block to be allocated.  If the portion exists but is too
	   *  small for a free block, recompute allocation pointer so that the
	   *  portion will be large enough.
	   */

	  cbBefore = pbAlloc - pb;
	  if(cbBefore > 0 && cbBefore < sizeof(VMKRHF))
	    {
	      pbAlloc = (char *)
		((ulong_t)(pb + sizeof(VMKRHF) + sizeof(VMKRHB) + cbAlign)
		  & ~cbAlign) - sizeof(VMKRHB);
	      cbBefore = pbAlloc - pb;
	    }

	  /*
	   *  Now check that we can still carve a large enough block to
	   *  satisfy the request, beginning from the point marked
	   *  by pbAlloc instead of from pb.  If the region between lower
	   *  and upper allocation limits is smaller than the rounded
	   *  request size, reject the block and keep searching.
	   */

	  if(pbAlloc + cbRound > pbAllocLimit)
	    {
	      continue;
	    }

	  /*
	   *  If request is for physical memory < 16 Meg, reject the block if
	   *  the allocated block would use existing page fragments assigned
	   *  to physical pages above 16 Meg.  It is likely that another block
	   *  will satisfy this constraint.
	   */

	  if((fl & PG_16M) && !(flCarve & krhb_CR_USEDECO))
	    {
	      pbData = pbAlloc + sizeof(VMKRHB);
	      pbDecommitMin = vmDecommitMinKRHB(pb);
	      if(pbData < pbDecommitMin)
		{
// perf - mem reduction : for retail and hstrict builds combine
//			  RES1MRO and RES1MRW and RESRO with RESRW
#ifndef VMSTRICT
		  if (fl & PG_1M) continue;
#endif
		  if(PGIsAbove16Meg((ulong_t) pbData) ||
		    ((char *) vmPageCeil(pbData) < pbDecommitMin &&
		      PGIsAbove16Meg((ulong_t) pbDecommitMin - PAGESIZE)))
		    {
		      continue;
		    }
		}
	      pbResid = pbAlloc + cbRound;
	      pbDecommitMax = vmDecommitMaxKRHB(pb, cbAvail);
	      if(pbResid > pbDecommitMax)
		{
// perf - mem reduction : for retail and hstrict builds combine
//			  RES1MRO and RES1MRW and RESRO with RESRW
#ifndef VMSTRICT
		  if (fl & PG_1M) continue;
#endif
		  if(PGIsAbove16Meg((ulong_t) pbResid - 1) ||
		    ((char *) vmPageFloor(pbResid - 1) > pbDecommitMax &&
		     PGIsAbove16Meg((ulong_t) pbDecommitMax)))
		    {
		      continue;
		    }
		}
	    }
	}

      /*
       *  The free block is usable for an allocation from the point
       *  marked by pbAlloc.  All of the free block's internal pages
       *  (if any) are decommitted.  Calculate which ones need to be
       *  committed.  We leave pages that will be internal to the
       *  residual free block as decommitted.
       */

      pbResid = pbAlloc + cbRound;
      cbResid = pb + cbAvail - pbResid;
      if(cbAvail > PAGESIZE + sizeof(VMKRHF))
	{
	  /*
	   *  There may be pages to commit.  Since this operation
	   *  may block, we need to take the heap semaphore.  Take
	   *  the semaphore if we can do so without having to block
	   *  otherwise wait for a clearsem and repeat the heap scan.
	   */
	  if ((rc = KSEMRequestMutex((HKSEM)&(pkrh->krh_ksem), 0))
			    == ERROR_TIMEOUT)
	    {
	      if ((rc = KSEMRequestMutex((HKSEM)&(pkrh->krh_ksem), -1))
		     != NO_ERROR)
		{
		  return(rc);
		}
	      KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
	      pbAlloc = NULL;
	      goto scanheap;
	    }

	  /*
	   *  Commit needed pages.  If error, give up.	Clear semaphore
	   *  afterwards since we will not yield.
	   */

	  rc = vmCommitSubAllocKRHB(pkrh, pb, pbAlloc, pbResid, cbAvail, fl);
	  KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
	  if(rc != NO_ERROR)
	    {
	      return(rc);
	    }
	}

      /*
       *  Relevant pages of free block are now committed.  Remove
       *  block from free list.
       */

      vmRemoveFreeKRHB(pkrh, pb);

      /*
       *  Now carve out preceding free block (if any), allocated block
       *  and residual free block.
       */

      if(cbBefore > 0)
	{
	  /*
	   *  There is to be a preceding free block.  Create this
	   *  by splitting the block into two free blocks.  The first
	   *  block will be added to the free list, but the residual
	   *  block is not added since it will be carved again.
	   */

	  vmCarveKRHB(pkrh,pb,cbBefore,cbAvail,pkhbaFree, krhb_CR_NOINSFR);
	    cbAvail -= cbBefore;
	}

      /*
       *  Carve out and allocate new block with size equal to the
       *  requested number of bytes.  Place any residual free block
       *  on the free list, leaving its internal pages decommitted.
       */

      vmCarveKRHB(pkrh, pbAlloc, cbRound, cbAvail, pkhbaAlloc, flCarve);

      /*
       *  If request is for user accessible memory (to allow ring 3
       *  device driver initialization routines to run), set the user
       *  bit on all of the heap block pages.  Note that this action
       *  causes pages shared by neighboring blocks to become user
       *  accessible, but the user access is removed from the heap
       *  after system init time, and it is preferable that PG_U
       *  requests do not force page alignment.
       */

      pbData = pbAlloc + sizeof(VMKRHB);

      if(fl & PG_U)
	{
	  rc = PGSetAccess(VMRegionBase((ulong_t) pbData) >> PAGESHIFT,
			   NA,
			   VMRegionSize((ulong_t) pbData,
			     cbRound - sizeof(VMKRHB)) >> PAGESHIFT,
			   (fl | pkrh->krh_fl) & (PG_U | PG_W));

	  /*
	   *  If an error occurs while setting the access permissions,
	   *  free the block.
	   */

	  if(rc != NO_ERROR)
	    {
	      VMFreeKHB(hkh, pbData);
	      return(rc);
	    }
	}

      /*
       *  Return pointer to new block's data.  Pointer points just past
       *  block header.  Increment heap modification count.
       *  Check integrity of heap following successful allocation
       *  of existing free block and return to caller.
       */

      pkhbi->khbi_pbData = pbData;
      ++pkrh->krh_cMods;
      vmAssert(vmIsValidKRH(pkrh));
      return(NO_ERROR);

    } /* end for */

  return(ERROR_NOT_ENOUGH_MEMORY);
}


/***EP	VMReallocKHB - Realloc Kernel Heap Block
 *
 *	This function reallocates a block allocated from the specified kernel
 *	heap.  If the new block size is larger than the original, an attempt
 *	is made to grow the block into a following free area.  If this is not
 *	possible, a new block is created, the data copied and the original
 *	block is freed.
 *
 *	WARNING:	A read-only heap block may not be grown.  It may
 *			be shrunk however.
 *
 *	int VMReallocKHB(VMHKH hkh, VMKHBI *pkhbi, ulong_t cb, ulong_t fl);
 *
 *	ENTRY		hkh		- Kernel heap header handle
 *					  created by VMCreateKH.  Special
 *					  values map the public heaps:
 *					  - VM_HKH_PUB_RESRW
 *					    Public Resident RW Heap
 *					  - VM_HKH_PUB_RESRO
 *					    Public Resident RO Heap
 *					  - VM_HKH_PUB_RES1MRW
 *					    Public Resident Lowmem RW Heap
 *					  - VM_HKH_PUB_RES1MRO
 *					    Public Resident Lowmem RO Heap
 *					  - VM_HKH_PUB_SWAPRW
 *					    Public Swappable RW Heap
 *					  - VM_HKH_PUB_SWAPRO
 *					    Public Swappable RO Heap
 *			pkhbi		- Pointer to location containing address
 *					  of block to be reallocated.  This
 *					  address changes if the block is
 *					  being grown and has to be reallocated.
 *			cb		- New size of block
 *			fl		- Allocation flags.
 *	RETURN		void		- function internal errors if an error
 *					  is detected.
 */

int VMReallocKHB(hkh, pkhbi, cb, fl)
VMHKH			hkh;		/* Heap header Handle */
VMKHBI			*pkhbi; 	/* Addr of block data pointer */
register ulong_t	cb;		/* Newsize requested in bytes */
ulong_t 		fl;		/* Alloc flags */
{
  VMKRH 		*pkrh;		/* Pointer to heap header */
  ulong_t		cbOld;		/* Original block size */
  char			*pb;		/* Pointer to block header */
  register char 	*pbNext;	/* Pointer to succeeding block */
  ulong_t		cbRound;	/* Rounded request size */
  ulong_t		cbHeader;	/* New block header size */
  ulong_t		cbAvail;	/* Available size for in-situ growth */
  ulong_t		cbNext; 	/* Effective next block size */
  ulong_t		flCarve;	/* Carving flags */
  int			rc;		/* Result code */
  VMKHBI		khbiLocal;	/* Heap block attributes */
  VMKHBI *pkhbiLocal = SSToDS(&khbiLocal);
					/* Heap block attributes */

  /*
   *  Verify that heap header handle is valid.
   */

  vmAssert(vmIsHKH(hkh));

  /*
   *  If swappable request, call swappable heap reallocation function.
   */

  if(vmIsHKSH(hkh))
    {
      return(vmReallocKSHB(hkh, (char **) pkhbi, cb, fl));
    }

  /*
   *  Validate heap handle and verify that heap is OK.
   */

  vmAssert(vmIsHKRH(hkh));
  pkrh = apkh[hkh].kh_pkrh;
  vmAssert(vmIsValidKRH(pkrh));

  /*
   *  Verify that request size is not larger than heap size, or so large
   *  as to cause overflow on rounding.
   */

  if(cb > (ulong_t)(pkrh->krh_pbLast - pkrh->krh_pbFirst))
    {
      return(ERROR_NOT_ENOUGH_MEMORY);
    }

  khbiLocal.khbi_pbData = pkhbi->khbi_pbData;
  pb = khbiLocal.khbi_pbData - sizeof(VMKRHB);
  cbHeader = sizeof(VMKRHB);

  /*
   *  If the block is attributed, check if it has mapped by a selector
   *  and if so, set the VMAC_SELALLOC bit in the flags.
   */

  flCarve = NA;
  if(vmIsAttrKRHB(pb))
    {
      /*
       *  BUGBUG IDB 9/26/90 The following statement trashes flat ES via
       *  push ss, pop es.  It is temporarily replaced with the
       *  vmCopyAttrKRHB macro call below it until the C compiler is fixed.
       *
       *  khbiLocal.khbi_khba = *(vmpkhbaKRHB(pb));
       */

      vmCopyAttrKRHB(khbiLocal.khbi_khba, *(vmpkhbaKRHB(pb)));
      if(khbiLocal.khbi_sel > 3)
	{
	  fl |= VMAC_SELALLOC;
	  flCarve |= krhb_ATTR;
	}
    }
  else
    {
      khbiLocal.khbi_sel = (SEL) NA;
      khbiLocal.khbi_hobOwner = ((VMKRHB *)(pb))->krhb_hobOwner;
    }

  cbRound = vmRoundReqKRHB(cb, fl);

  /*
   *  Check that specified block pointer is within the specified resident heap.
   *  Check that it is dword aligned, point at preceding dword and check that
   *  that it contains a valid signature for a resident heap block header.
   */

  vmAssert(pb >= pkrh->krh_pbFirst &&
	   pb < pkrh->krh_pbEndRes &&
	   ((ulong_t)(pb) & 3) == 0 &&
	   !vmIsFreeKRHB((pb)));

  cbOld = vmSizeKRHB(pb);
  rc = NO_ERROR;

  /*
   *  If new block size is less than old, shrink block in situ.
   */

  if(cbRound < cbOld)
    {
      VMShrinkKHB(hkh, pkhbi->khbi_pbData, cb);
    }

  /*
   *  Else if new block size > old, try to grow the block in place and if
   *  that fails, allocate new block, copy old to new and free old.
   */

  else if(cbRound > cbOld)
    {
      /*
       *  Take the heap semaphore.  Terminate if we are interrupted while
       *  waiting for it.  Need to wait for short-term locks to clear
       *  in case we decommit free pages.
       */

	if ((rc = KSEMRequestMutex((HKSEM)&(pkrh->krh_ksem), -1))
			    != NO_ERROR)
	{
	  return(rc);
	}

      /*
       *  If succeeding block is free, consider growth in situ.  If
       *  PG_CONTIG or PG_16M is specified, growth in situ is not possible
       *  since we may cross a page boundary in the next free block, and the
       *  next page is probably not contiguous or physical < 16M.
       */

      pbNext = vmNextKRHB(pb);
      if(vmCanCoalesceKRHB(pbNext) && !(fl & (PG_CONTIG | PG_16M)))
	{
	  cbNext = vmSizeKRHB(pbNext);
	  cbAvail = cbOld + cbNext;

	  /*
	   *  If there is enough room for the requested block to grow
	   *  in situ, proceed.
	   */

	  if(cbRound <= cbAvail)
	    {
	      /*
	       *  Attempt to commit needed pages in next block.
	       */

	      if(cbNext > PAGESIZE + sizeof(VMKRHF))
		{
		  if((rc = vmCommitSubKRHB(pkrh,
				    pbNext,
				    cbRound - cbOld,
				    cbNext, fl)) != NO_ERROR)
		    {
		      KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
		      return(rc);
		    }
		}

	      /*
	       *  Coalesce with the next block.
	       */

	      vmCoalesceKRHB(pkrh, pb, NA);
	      pbNext = pb;

	      /*
	       *  Carve new larger block, possibly creating residual free
	       *  block.
	       */

	      vmCarveKRHB(pkrh, pb, cbRound, cbAvail,
			  SSToDS(&khbiLocal.khbi_khba), flCarve);

	      /*
	       *  Clear heap semaphore and return.
	       */

	      vmAssert(vmIsValidKRH(pkrh));
	      KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
	      return(rc);
	    }
	}

      /*
       *  We need to allocate a new block.  If successful, copy data
       *  from old block and free old block.
       */

      if((rc = VMAllocKHB(hkh, cb, vmOwnerKRHB(pb), fl,
	 pkhbiLocal)) == NO_ERROR)
	{
	  cbOld -= ((vmIsRegKRHB(pb)) ?
	    sizeof(VMKRHB) : (sizeof(VMKRHB) + sizeof(VMKHBA)));
	  rc = vmCopyKHB(pkhbiLocal->khbi_pbData,
			 pkhbi->khbi_pbData,
			 cbOld, pkrh->krh_fl & (PG_MODIFIER | PG_PERM));
	  if(rc == NO_ERROR)
	    {
  //
  //  148751 change to not free the old block until the address of the
  //	     new block has been saved.
  //
	       pb = pkhbi->khbi_pbData;   /* save addr of old block  148751 */
	       pkhbi->khbi_pbData = pkhbiLocal->khbi_pbData;	  /* 148751 */
	     }
	  else
	    {
	      pb = pkhbiLocal->khbi_pbData;  /* get addr of new block 148751 */
	    }
	  VMFreeKHB(hkh, pb);		   /* free correct data block 148751 */
	}

      /*
       *  Clear the heap semaphore.
       */

      KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
    }

  /*
   *  Validate heap handle and verify that heap is OK.	Return to caller.
   */

  vmAssert(vmIsValidKRH(pkrh));
  return(rc);
}


/***EP	VMFreeKHB - Free Kernel Heap Block
 *
 *	This function frees a block allocated from the specified kernel
 *	heap.  The block owner is changed to the free owner.  Coalescing of
 *	succeeding free blocks (if any) is performed, and the resulting
 *	merged block is then placed on the appropriate free list.  All pages
 *	internal to the block that do not overlap with the header, free list
 *	links, back link or attribute area are automatically decommitted.
 *
 *	void VMFreeKHB(VMHKH hkh, char *pbData)
 *
 *	ENTRY		hkh		- Kernel heap header handle
 *					  created by VMCreateKH.  Special
 *					  values map the public heaps:
 *					  - VM_HKH_PUB_RESRW
 *					    Public Resident RW Heap
 *					  - VM_HKH_PUB_RESRO
 *					    Public Resident RO Heap
 *					  - VM_HKH_PUB_RES1MRW
 *					    Public Resident Lowmem RW Heap
 *					  - VM_HKH_PUB_RES1MRO
 *					    Public Resident Lowmem RO Heap
 *					  - VM_HKH_PUB_SWAPRW
 *					    Public Swappable RW Heap
 *					  - VM_HKH_PUB_SWAPRO
 *			pbData		- Pointer to data ofblock to be freed
 *
 *	RETURN		void		- Any error is an internal error
 *
 *	WARNING 	This routine may block.
 */

void			VMFreeKHB(hkh, pbData)
VMHKH			hkh;		/* Heap header handle */
char			*pbData;	/* Pointer to data of block to free */
{
  register VMKRH	*pkrh;		/* Pointer to heap header */
  register char 	*pb;		/* Pointer to block header */
  int			rc;		/* Result code */

  /*
   *  Verify that heap header handle is valid.
   */

  vmAssert(vmIsHKH(hkh));

  /*
   *  If swappable request, call swappable heap reallocation function.
   */

  if(vmIsHKSH(hkh))
    {
      vmFreeKSHB(hkh, pbData);
      return;
    }

  /*
   *  Validate heap handle and verify that heap is OK.
   */

  vmAssert(vmIsHKRH(hkh));
  pkrh = apkh[hkh].kh_pkrh;
  pb = pbData - sizeof(VMKRHB);

  /*
   *  Take the heap semaphore, ignoring interrupts since we are freeing
   *  resources.  Since the heap is permanent, any error is an internal
   *  error.
   */
  while ( (KSEMRequestMutex((HKSEM)&(pkrh->krh_ksem), -1)) != NO_ERROR);

  vmAssert(vmIsValidKRH(pkrh));

  /*
   *  Check that specified block pointer is within the specified resident heap.
   *  Check that it is dword aligned, point at preceding dword and check that
   *  that it contains a valid signature for a resident heap block header.
   */

  vmAssert(vmIsWithinKRH(pkrh, pb) &&
	   (((ulong_t)(pb)) & krhb_STATUS_MASK) == 0 &&
	   !vmIsFreeKRHB(pb));

  /*
   *  Optimize the case where we are freeing a regular block and neither
   *  neighbor is free by not calling the coalesce routine in that case.
   */

  vmSetFreeKRHB(pkrh, pb);
  if(vmIsRegKRHB(pb) &&
     (!vmIsFreeKRHB(vmNextRegKRHB(pb))) &&
     (!vmHasPrevFreeRegKRHB(pb)))
    {
      if(vmSizeRegKRHB(pb) > PAGESIZE)
	{
	  vmDecommitKRHB(pb);
	}
      vmInsertFreeKRHB(pkrh, pb);

      /*
       *  Validate heap handle and verify that heap is OK.  Clear semaphore
       *  and return.
       */

      vmAssert(vmIsValidKRH(pkrh));
      KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
      return;
    }

  /*
   *  A neighbor is free.  Coalesce if no one has yielded at them and
   *  insert resulting block on free list with internal pages (if any)
   *  decommitted.
   */

  vmCoalesceKRHB(pkrh, pb, VMS_DECOMMIT);

  /*
   *  Validate heap handle and verify that heap is OK.	Clear semaphore
   *  and return.
   */

  vmAssert(vmIsValidKRH(pkrh));
  KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
}


/***EP	VMShrinkKHB - Shrink Kernel Heap Block
 *
 *	This function shrinks a block allocated from the specified kernel
 *	heap.  If there is sufficient space freed up to create a new free
 *	block, one is created, otherwise, no shrinkage takes place.
 *	The block start address remains unchanged after shrinking.
 *
 *	void VMShrinkKHB(VMHKH hkh, char *pbData, ulong_t cb)
 *
 *	ENTRY		hkh		- Kernel heap header handle
 *					  created by VMCreateKH.  Special
 *					  values map the public heaps:
 *					  - VM_HKH_PUB_RESRW
 *					    Public Resident RW Heap
 *					  - VM_HKH_PUB_RESRO
 *					    Public Resident RO Heap
 *					  - VM_HKH_PUB_RES1MRW
 *					    Public Resident Lowmem RW Heap
 *					  - VM_HKH_PUB_RES1MRO
 *					    Public Resident Lowmem RO Heap
 *					  - VM_HKH_PUB_SWAPRW
 *					    Public Swappable RW Heap
 *					  - VM_HKH_PUB_SWAPRO
 *			pbData		- Pointer to data of block to be shrunk
 *			cb		- New block size
 *
 *	RETURN		void		- Function internal errors if an error
 *					  is detected.
 */

void			VMShrinkKHB(hkh, pbData, cb)
VMHKH			hkh;		/* Heap header handle */
char			*pbData;	/* Pointer to block to free */
register ulong_t	cb;		/* New size of block */
{
  register VMKRH	*pkrh;
  char			*pbOldNext;
  char			*pbOldNextDecommitMax;
  char			*pbDecommitMin;
  char			*pbDecommitMax;

  ulong_t		cbOld;
  ulong_t		cbOldNext;
  ulong_t		cbRound;	/* New total rounded size of block */
  register char 	*pb;		/* Pointer to block header */
  VMKHBA		khba;		/* Heap block attributes */
  int			rc;		/* Result code */
  SEL			sel;		/* Selector mapping block (if any) */
  ulong_t		fl = NA;	/* Flags for rounding */
  ulong_t		flCarve = NA;	/* Flags for carving */

  /*
   *  Verify that heap header handle is valid.
   */

  vmAssert(vmIsHKH(hkh));

  /*
   *  If swappable request, call shrink swappable heap block function.
   */

  if(vmIsHKSH(hkh))
    {
      vmShrinkKSHB(hkh, pbData, cb);
      return;
    }

  /*
   *  Validate heap handle and verify that heap is OK.
   */

  vmAssert(vmIsHKRH(hkh));
  pkrh = apkh[hkh].kh_pkrh;

  /*
   *  Take the heap semaphore.	Ignore interrupts since we are releasing
   *  resources.
   */
  while ( (KSEMRequestMutex((HKSEM)&(pkrh->krh_ksem), -1)) != NO_ERROR);
  vmAssert(vmIsValidKRH(pkrh));

  /*
   *  Check that the block pointer points within the specified heap, the
   *  block has a valid signature and the specified new size does not
   *  exceed the existing size.
   */

  pb = pbData - sizeof(VMKRHB);
  cbOld = vmSizeKRHB(pb);
  vmAssert(vmIsWithinKRH(pkrh, pb));
  vmAssert(vmIsValidKRHB(pb));
  vmAssert(cb <= cbOld);

  if(vmIsAttrKRHB(pb))
    {
      sel = vmpkhbaKRHB(pb)->khba_sel;
      if((((ulong_t) sel) & ~(krhb_PBBACK | krhb_ATTR)) != 0)
	{
	  fl |= VMAC_SELALLOC;
	  flCarve |= krhb_ATTR;
	}
    }
  cbRound = vmRoundReqKRHB(cb, fl);

  /*
   *  Shrink the block, creating a residual free block if sufficient space
   *  is released.  Decommit internal pages of residual free block (if any).
   */

  if(cbRound + sizeof(VMKRHF) <= cbOld)
    {
      if(vmIsRegKRHB(pb))
	{
	  khba.khba_sel = (SEL) NA;
	  khba.khba_hobOwner = ((VMKRHB *)(pb))->krhb_hobOwner;
	}
      else
	{
	  /*
	   *  BUGBUG IDB 9/26/90 The following statement trashes flat ES via
	   *  push ss, pop es.	It is temporarily replaced with the
	   *  vmCopyAttrKRHB macro call below it untile the C compiler is fixed.
	   *
	   *  khba = *(vmpkhbaKRHB(pbData - sizeof(VMKRHB)));
	   */

	  vmCopyAttrKRHB(khba, *(vmpkhbaKRHB(pbData - sizeof(VMKRHB))));
	}

      /*
       *  Calculate the upper limit of the region to be decommitted.
       *  If the next block will not be coalesced, the upper bound is
       *  the upper limit of the interior region of the present block,
       *  otherwise, the upper limit is the minimum of the upper and
       *  lower bounds for the interior region of the next block.
       */

      pbDecommitMax = vmDecommitMaxKRHB(pb, cbOld);

      /*
       *  If the next block is free and will be coalesced, remember the
       *  start of its decommitted region.
       */

      pbOldNext = pb + cbOld;
      if(vmCanCoalesceKRHB(pbOldNext))
	{
	  cbOldNext = vmSizeKRHB(pbOldNext);
	  pbDecommitMax = vmDecommitMinKRHB(pbOldNext);
	  pbOldNextDecommitMax = vmDecommitMaxKRHB(pbOldNext, cbOldNext);
	  if(pbOldNextDecommitMax < pbDecommitMax)
	    {
	      pbDecommitMax = pbOldNextDecommitMax;
	    }
	}

      /*
       *  Set the minimum bound for the region to be committed equal
       *  to the start of the interior region (if any) of the new
       *  residual block.
       */

      pbDecommitMin = vmDecommitMinKRHB(pb + cbRound);

      /*
       *  Shrink the block, coalescing residual free part.
       */

      vmCarveKRHB(pkrh, pb, cbRound, cbOld, SSToDS(&khba), flCarve);

      /*
       *  The start of the next block may have moved down, so its interior
       *  region may also have grown down.  Decommit any new pages belonging
       *  to the region.
       */

      if(pbDecommitMin < pbDecommitMax)
	{
	  while((rc = PGFree(((ulong_t) pbDecommitMin) >> PAGESHIFT,
			      NULL,
			     (pbDecommitMax - pbDecommitMin) >> PAGESHIFT,
			     PG_CHKLOCK)) == ERROR_INTERRUPT);
	  vmAssert(rc == NO_ERROR);
	}
    }

  /*
   *  Validate heap handle and verify that heap is OK.	Clear semaphore
   *  and return.
   */

  vmAssert(vmIsValidKRH(pkrh));
  KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
}


/***EP	VMGetKHBI - Get Kernel Heap Block Information
 *
 *	This function determines if a linear address lies within a kernel
 *	heap and, if so, returns information about that heap, and also the
 *	information about the heap block whose data begins at the given
 *	address.
 *
 *	int VMGetKHBI(ulong_t fl, VMKHBI *pkhbi)
 *
 *	ENTRY		fl		Flags
 *					  VMKHBI_HEAP
 *					    Get info about heap (if any) in
 *					    which given address lies
 *					  VMKHBI_BLOCK
 *					    Get info about heap block.
 *					    WARNING - If this flag is specified
 *					    the given address must point to
 *					    a heap block's data unless the
 *					    VMKHBI_UNALIGN flag is also
 *					    specified.
 *					  VMKHBI_UNALIGN
 *					    Given linear address does not
 *					    necessarily point to the start
 *					    of a heap block's data.
 *					    WARNING!  Use this flag only
 *					    if absolutely necessary as a
 *					    linear scan of the heap results!
 *			pkhbi		Pointer to Heap Block Info
 *					  The block's data address must be
 *					  stored in the khbi_pbData field.
 *	EXIT		pkhbi		Pointer to Heap Block Info
 *					  The info will be filled in if the
 *					  address lies within a heap, assuming
 *					  that the address points to the
 *					  start of a heap block's data.
 *	RETURN		int		Result code (NO_ERROR = success)
 */

int VMGetKHBI(fl, pkhbi)
ulong_t 		fl;		/* Flags */
VMKHBI			*pkhbi; 	/* Address of heap block info */
{
  register VMHKH	hkh;		/* Heap handle */
  VMKSHD		*pkshd; 	/* Ptr to swappable heap block desc */
  register VMKRH	*pkrh;		/* Ptr to resident heap header */
  VMKSH 		*pksh;		/* Ptr to swappable heap header */
  char			*pbData;	/* Ptr to block's data */
  int			rc;		/* Result code */
  VMKSHB		*pkshb;

  /*
   *  Return an error if given linear address is outside all of the
   *  heaps.
   */

  if(!vmPossWithinKH(pkhbi->khbi_pbData))
    {
      return(ERROR_INVALID_ADDRESS);
    }

  pbData = pkhbi->khbi_pbData;

  /*
   *  Validate flags.  Check that only permitted flags are specified.  It
   *  is only meaningful to specify VMKHBI_UNALIGN if asking for heap block
   *  info.
   */

  vmAssert(fl && fl == (fl & (VMKHBI_HEAP | VMKHBI_BLOCK | VMKHBI_UNALIGN)));
  vmAssert((fl & VMKHBI_UNALIGN) == 0 || fl & VMKHBI_BLOCK);

  /*
   *  Search the heap header array.  Note that entry 0 is not used.
   */

  for(hkh = hkh_MIN; hkh <= hkhCurrMax; hkh++)
    {
      /*
       *  If the flags word is zero, this heap slot is not in use.
       */

      if(apkh[hkh].kh_fl == NA)
	{
	  continue;
	}

      if(vmIsHKRH(hkh))
	{
	  /*
	   *  This is a resident heap header handle.  Compare given address
	   *  with minimum and maximum block addresses in the heap.
	   *  If within range, copy handle and flags from heap header
	   *  into info buffer and return success.
	   */

	  pkrh = apkh[hkh].kh_pkrh;
	  if(pbData >= pkrh->krh_pbFirst && pbData < pkrh->krh_pbEndRes)
	    {
	      if(fl & VMKHBI_HEAP)
		{
		  pkhbi->khbi_hkh = hkh;
		  pkhbi->khbi_fl = pkrh->krh_fl;
		}

	      /*
	       *  If requested, fill in information related to specific block.
	       */

	      if(fl & VMKHBI_BLOCK)
		{
		  if(fl & VMKHBI_UNALIGN)
		    {
		      /*
		       *  Given linear address is not necessarily aligned
		       *  to the start of a heap block's data.  Scan
		       *  heap to find enclosing block.
		       */

		      if((rc = vmFindKRHB(pkhbi)) != NO_ERROR)
			{
			  return(rc);
			}
		      pbData = pkhbi->khbi_pbData;
		    }

		  if(vmIsAttrKRHB(pbData -  sizeof(VMKRHB)))
		    {
		      pkhbi->khbi_khba = *(vmpkhbaKRHB(pbData-sizeof(VMKRHB)));
		    }
		  else
		    {
		      pkhbi->khbi_hobOwner =
			vmOwnerRegKRHB(pbData - sizeof(VMKRHB));
		      pkhbi->khbi_sel = NA;
		    }
		}
	      return(NO_ERROR);
	    }
	}
      else
	{
	  /*
	   *  This must be a swappable heap header handle.  Internal error
	   *  if it is not.  Compare given address with minimum and maximum
	   *  block addresses in the heap.  If within range, copy handle and
	   *  flags from heap header into info buffer and return success.
	   */

	  vmAssert(vmIsHKSH(hkh));
	  pksh = apkh[hkh].kh_pksh;
	  if(pbData >= pksh->ksh_pbStart &&
	     pbData < pksh->ksh_pbEndCom)
	    {
	      pkhbi->khbi_hkh = hkh;
	      if(fl & VMKHBI_HEAP)
		{
		  pkhbi->khbi_fl = pksh->ksh_fl;
		}

	      /*
	       *  If requested, fill in information related to specific block.
	       */

	      if(fl & VMKHBI_BLOCK)
		{
		  if(fl & VMKHBI_UNALIGN)
		    {
		      /*
		       *  Given linear address is not necessarily aligned
		       *  to the start of a heap block's data.  Scan
		       *  heap to find enclosing block.
		       */

		      if((rc = vmFindKSHB(pkhbi)) != NO_ERROR)
			{
			  return(rc);
			}
		      pbData = pkhbi->khbi_pbData;
		    }

		  pkshb = (VMKSHB *)(pkhbi->khbi_pbData - sizeof(VMKSHB));
		  pkhbi->khbi_hobOwner = pkshb->kshb_hobowner;
		  pkhbi->khbi_sel = pkshb->kshb_sel;
		}
	      return(NO_ERROR);
	    }
	}
    }
  return(ERROR_INVALID_PARAMETER);
}


/***EP	VMMoveKHB - Move swappable heap objects to kernel resident heap.
 *
 *	VMMoveKHB is called during initialization to permanently lock down
 *	Device Driver segments allocated from a swappable heap by moving
 *	them to a resident heap.  If a low physical memory allocation is
 *	required, the destination must be a low memory heap.
 *
 *	int VMMoveKHB(VMHKH hkhSource, VMHKH hkhDest, ulong_t fl, VMKHBI *pkhbi)
 *
 *	ENTRY		hkhSource	- Handle of swappable heap to move from
 *			hkhDest 	- Handle of resident heap to move to
 *			fl		- Flags
 *					    VMAC_ALIGNPAGE
 *					    PG_CONTIG
 *					    PG_16M
 *					    PG_U
 *			pkhbi		- Pointer to address of heap block data
 *
 *	WARNING 	This function changes the linear address of the heap
 *			block that is moved.
 */


int	VMMoveKHB(hkhSource, hkhDest, fl, pkhbi)
VMHKH			hkhSource;	/* Swappable heap header handle */
VMHKH			hkhDest;	/* Resident heap header handle */
ulong_t 		fl;		/* Flags specific to this request */
VMKHBI			*pkhbi; 	/* Pointer to heap block */
{
  register int		rc;		/* Return code */
  VMKSH 		*pksh;
  VMKSHB		*pkshb;
  VMKHBI		khbiNew;
  ulong_t		cb;
  register VMKRH	*pkrh;
  VMKHBI		*pkhbiNew = SSToDS(&khbiNew);

  /*
   *  Validate heap header handles
   */

  vmAssert(vmIsHKH(hkhSource));
  vmAssert(vmIsHKH(hkhDest));
  vmAssert(vmIsHKSH(hkhSource));
  vmAssert(vmIsHKRH(hkhDest));

  /*
   *  Get the pointer to the swappable block
   */

  pksh = apkh[hkhSource].kh_pksh;

  pkshb = (VMKSHB *)(pkhbi->khbi_pbData - sizeof(VMKSHB));

  /*
   *  If the block is selector mapped, copy selector into info and set flag.
   */

  khbiNew.khbi_sel = pkshb->kshb_sel;
  if(khbiNew.khbi_sel > 3)
    {
      fl |= VMAC_SELALLOC;
    }

  /*
   *  Allocate a resident block of the same size
   */

  cb = kshbSize(pkshb) - sizeof(VMKSHB);

  if((rc = VMAllocKHB(hkhDest, cb, pkshb->kshb_hobowner, fl,pkhbiNew))
       == NO_ERROR)
    {
      /*
       *  Copy from swappable heap. If read only resident heap, temporarily
       *  change the state of the pages to writable.
       */

      pkrh = apkh[hkhDest].kh_pkrh;
      if((rc = vmCopyKHB(khbiNew.khbi_pbData, pkhbi->khbi_pbData, cb,
	   ((fl | pkrh->krh_fl) & (PG_MODIFIER | PG_PERM)))) == NO_ERROR)
	{
	  VMFreeKHB(hkhSource, pkhbi->khbi_pbData);
	  pkhbi->khbi_pbData = khbiNew.khbi_pbData;
	}
      else
	{
	  VMFreeKHB(hkhDest, khbiNew.khbi_pbData);
	}
    }
  return(rc);
}


/***LP	vmFindKRHB - Find Kernel Resident Heap Block
 *
 *	Given a linear address within the data of a kernel resident heap
 *	block, this function finds the linear address of the block that
 *	contains the given address.  An error is returned if the given
 *	address does not reside within the data portion of a heap block.
 *
 *	int vmFindKRHB(VMKHBI *pkhbi)
 *
 *	ENTRY		pkhbi		- Pointer to heap block info
 *					  containing heap handle and
 *					  linear address
 *	EXIT		pkhbi		- linear address is updated
 *					  to point at start of block's data
 */

int vmFindKRHB(pkhbi)
VMKHBI			*pkhbi; 	/* Pointer to heap block info */
{
  VMKRH 		*pkrh;		/* Pointer to heap header */
  register char 	*pb;		/* Pointer to heap block */
  register char 	*pbNext;	/* Pointer to next heap block */
  register char 	*pbData;	/* Pointer to heap block's data */

  pkrh = apkh[pkhbi->khbi_hkh].kh_pkrh;
  pbData  = pkhbi->khbi_pbData;
  pb = pkrh->krh_pbFirst;

  do
    {
      /*
       *  If the next heap block starts at or beyond the given linear
       *  address, verify that the given address lies within the data
       *  of the current block (remebering that attributed blocks have
       *  an attribute area at the end of the block.
       */

      if((pbNext = vmNextKRHB(pb)) >= pbData)
	{
	  if(pbData >= pb + sizeof(VMKRHB) &&
	      (pbData < ((vmIsRegKRHB(pb)) ?
		pbNext : pbNext - sizeof(VMKHBA))))
	    {
	      pkhbi->khbi_pbData = pb + sizeof(VMKRHB);
	      return(NO_ERROR);
	    }
	  break;
	}
    }
  while((pb = pbNext) != pkrh->krh_pbLast);
  return(ERROR_INVALID_ADDRESS);
}


/***LP	vmCopyKHB - Copy one heap block to another
 *
 *	vmCopyKHB copies one heap block to another, setting and resetting
 *	the writable bit in the page tables if necessary.
 *
 *	int vmCopyKHB(void *pbDataDest, void *pbDataSrc, ulong_t cb, fl)
 *
 *	ENTRY		*pbDataSrc	- Pointer to source heap block
 *			*pbDataDest	- Pointer to destination flag
 *			cb		- Count of bytes
 *			fl		- Destination heap access flags
 *	RETURN		int		- Result code (NO_ERROR if success)
 */

int	 vmCopyKHB(pbDataDest, pbDataSrc, cb, fl)
char			*pbDataDest;
char			*pbDataSrc;
ulong_t 		cb;
ulong_t 		fl;
{
#ifdef VMSTRICT
  int			rc = NO_ERROR;
  if(!(fl & PG_W))
    {
      rc = PGSetAccess(((ulong_t)(pbDataDest)) >> PAGESHIFT, pPTDACur,
	    (((ulong_t)(pbDataDest) & PAGEMASK) + cb + PAGEMASK) >> PAGESHIFT,
	    (fl & PG_W) | PG_W);
    }

  if(rc == NO_ERROR)
    {
      memcpy(pbDataDest, pbDataSrc, cb);
    }

  if(!(fl & PG_W))
    {
      rc = PGSetAccess(((ulong_t)(pbDataDest)) >> PAGESHIFT, pPTDACur,
	    (((ulong_t)(pbDataDest) & PAGEMASK) + cb + PAGEMASK) >> PAGESHIFT,
	    fl & (PG_W | PG_U));
      vmAssert(rc == NO_ERROR);
    };
  return(rc);
#else
      memcpy(pbDataDest, pbDataSrc, cb);
      return(0);
#endif
}




/***LP	vmPhysCheckKRHB - Check physical memory properties for KRHB
 *
 *	This function verifies that a range of committed linear memory
 *	has the necessary properties for a heap block allocation of the
 *	given type.
 *
 *	ENTRY		fl		- VMAllocKHB flags
 *			pbData		- Pointer to start of region
 *			cbData		- Size of region in bytes
 */

int vmPhysCheckKRHB(fl, pbData, cbData)
ulong_t 		fl;		/* VMAllocKHB flags */
char			*pbData;	/* Base address of region */
ulong_t 		cbData; 	/* Size of region */
{
  ulong_t		laddrPage;	/* Page boundaries */

// perf - mem reduction : for retail and hstrict builds combine
//			  RES1MRO and RES1MRW and RESRO with RESRW
#ifndef VMSTRICT
  if(fl & PG_1M)
      return(FALSE);
#endif

  /*
   *  If a contiguous allocation request, check that no page
   *  boundary is crossed.
   */

  if((fl & PG_CONTIG) && vmCrossesPgBdry(pbData, cbData))
    {
      return(FALSE);
    }

  /*
   *  If a request for physical memory < 16M, call the Page Manager
   *  to verify that the committed memory already has this property.
   */

  if(fl & PG_16M)
    {
      laddrPage = VMRegionBase((ulong_t) pbData);
      pbData += cbData;
      do
	{
	  if(PGIsAbove16Meg(laddrPage))
	    {
	      return(FALSE);
	    }
	}
      while((laddrPage += PAGESIZE) < (ulong_t) pbData);
    }
  return(TRUE);
}


/***LP	vmCreateKRHB - Create Kernel Resident Heap Block
 *
 *	This function creates a kernel resident heap block.  The header is
 *	initialized at the beginning of the block and, if the block will be
 *	attributed, the attribute area at the end of the block is filled in.
 *	If the new block is to be free, and the memory following the block is
 *	to be considered as being a valid block, that block will automatically
 *	be coalesced if it is free.
 *
 *	void vmCreateKRHB(VMKRH *pkrh,
 *		     char *pb,
 *		     ulong_t cb,
 *		     VMKHBA pkhba,
 *		     ulong_t fl)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to block header
 *					  word containing size of block.
 *			cb		- Size of block including header
 *					  Must allow 4 extra bytes if a
 *					  large block is needed.
 *			pkhba		- Pointer to block's attributes
 *					  (selector and hobowner)
 *			fl		- Flags (optional)
 *					  - krhb_CR_NEW
 *					      block is new (no existing block
 *					      starts exactly at the given
 *					      address)
 *					  - krhb_CR_NXVAL
 *					      block is followed by a valid
 *					      block
 *					  - krhb_CR_NOMERGE
 *					      newly created free block is not
 *					      to be merged with a succeeding
 *					      free block.
 *					  - krhb_ATTR
 *					      block is attributed regardless
 *					      of its size.
 *					  - krhb_CR_NOINSFR
 *					      Do not add free block to
 *					      free list because it will
 *					      be carved again.
 *
 *	RETURN		void
 *
 *	CALLS		vmIsFreeKRHB, vmIsAttrKRHB, vmFreeKHB, vmSizeKRHB,
 *			vmWriteULKRHB, vmRegHdrValueKRHB, vmNeedsAttrKRHB,
 *			vmInsertFreeKRHB, vmRemoveFreeKRHB
 *
 *	EFFECTS 	Changes the heap
 *
 *	WARNING 	The caller is responsible for ensuring that
 *			an existing free block has been removed from
 *			the free list before calling this function.
 */

void vmCreateKRHB(pkrh, pb, cb, pkhba, fl)
register VMKRH		*pkrh;		/* Pointer to heap header */
register char		*pb;		/* Pointer to start of block */
register ulong_t	cb;		/* Desired block size including header
					   (and attribute data if ext block ) */
VMKHBA			*pkhba; 	/* Block's attributes (hobOwner, sel) */
ulong_t 		fl;		/* Flags */
{
  char			*pbNext;	/* Pointer to start of next block */
  ulong_t		flNew;		/* New block's flags */
  ulong_t		ulHdrValue;	/* Content of block header field */
  ushort_t		c = 0;		/* Case variable */

  /*
   *  Check that only permitted flags have been specified.
   */

  vmAssert(fl ==
	   (fl & (krhb_CR_NEW |
		  krhb_CR_USEDECO |
		  krhb_ATTR |
		  krhb_CR_NXVAL |
		  krhb_CR_NOMERGE |
		  krhb_CR_NOINSFR)));

  /*
   *  If creating a free block and the succeeding block is also valid and free
   *  and no one has yielded at it, remove it from the free list and merge it
   *  in with the new block being created except in special cases where the
   *  krhb_CR_NOMERGE flag is set.
   */

  pbNext = pb + cb;
  if(((fl & (krhb_CR_NXVAL | krhb_CR_NOMERGE)) == krhb_CR_NXVAL) &&
     pkhba->khba_hobOwner == VMKRHFOWNER && vmCanCoalesceKRHB(pbNext))
    {
      vmRemoveFreeKRHB(pkrh, pbNext);
      cb += vmSizeKRHB(pbNext);
    }

  /*
   *  Calculate flags and addresses for new block.  There are six cases:
   *
   *  If creating a block over an existing block:
   *  (0)   Old block is regular, new block will be regular
   *  (1)   Old block is regular, new block will be attributed
   *  (2)   Old block is attributed, new block will be regular
   *  (3)   Old block is attributed, new block will be attributed
   *  If creating a new scratch block
   *  (4)   New block will be regular
   *  (5)   New block will be attributed
   *
   *  When creating a block over an existing block, the setting of the
   *  krhb_PFREE flag in the header or prefix header pointed to by pb
   *  must be preserved.
   */

  if((fl & krhb_ATTR) || cb > krhb_REG_MAX_SIZE)
    {
      c += 1;
    }
  if(fl & krhb_CR_NEW)
    {
      c += 4;
    }
  else if(vmIsAttrKRHB(pb))
    {
      c += 2;
    }

  vmAssert(c <= 5);
  flNew = NA;
  switch(c)
    {
      case 0:	/* Existing block regular, new block regular */
	flNew = (vmHdrKRHB(pb) & krhb_PFREE);
	break;

      case 1:	/* Existing block regular, new block attributed */
	flNew = ((vmHdrKRHB(pb) & krhb_PFREE) ?
		 (krhb_ATTR_PFREE | krhb_ATTR) : krhb_ATTR);
	break;

      case 2:	/* Existing block attributed, new block regular */
	flNew = ((vmHdrKRHB(pb) & krhb_ATTR_PFREE) ? krhb_PFREE : NA);
	break;

      case 3:	/* Existing block attributed, new block attributed */
	flNew = ((vmHdrKRHB(pb) & krhb_ATTR_PFREE) ?
		 krhb_ATTR_PFREE | krhb_ATTR : krhb_ATTR);
	break;

      case 4: /* Scratching new regular block */
	flNew = NA;
	break;

      case 5: /* Scratching new attributed block */
	flNew = krhb_ATTR;
	break;
    }

  /*
   *  Form new block header.
   */

  ulHdrValue = vmMakeHdrRegKRHB(pkhba->khba_hobOwner, cb, flNew);
  if(flNew & krhb_ATTR)
    {
      ulHdrValue = vmMakeHdrAttrKRHB(cb, flNew);
    }

  /*
   *  Write new block header word.
   */

  vmWriteULKRHB(pkrh, pb, ulHdrValue);

  /*
   *  If block is attributed, write attribute area.
   */

  if(flNew & krhb_ATTR)
    {
      vmSetAttrKRHB(pkrh, pb, pkhba);
    }

  /*
   *  If new block is free, add it to the free list unless requested not to
   *  do so (case where block will be carved again).
   */

  if((pkhba->khba_hobOwner == VMKRHFOWNER) && !(fl & krhb_CR_NOINSFR))
    {
      vmInsertFreeKRHB(pkrh, pb);
    }
}



/***LP	vmYieldKRHB - yield during alloc
 *
 *	This function is called to yield during an allocation from the kernel
 *	resident heap.
 *
 *	vmYieldKRHB (pkrh, pb)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to block header
 *	RETURN		char *		- Pointer to block at which to resume
 *					  A null pointer is returned if the
 *					  search is to recommence at the start
 *					  of the first eligible free list)
 *
 *	If the yield bit is already set in the block's header, then
 *	scan the yield array looking for a matching entry.  When it is
 *	found, increment the yield count.  If the yield bit is not
 *	already set, then scan the array for an empty slot.  If one is
 *	found, then store the block pointer in it and set the yield
 *	count to one.  Call TKResched.	If no slot was found, then
 *	return pbKRHSirstFree.	Otherwise, set the return pointer to
 *	the value stored in the array entry and decrement the
 *	corresponding yield count.  If the count is zero, then clear
 *	the pointer field in the entry and clear the yield bit in the
 *	block header.  Return the block pointer.
 *
 *	CALLS		TKResched, vmSetYieldKRHB, vmClrYieldKRHB,
 *			vmHasYieldedKRHB
 *
 *	WARNING 	This function depends on non-preemptibility while
 *			manipulating the yield array.
 *
 *	EFFECTS 	Yield array entry, yield bit in block header.
 */

char			*vmYieldKRHB(pkrh, pb)
register VMKRH		*pkrh;		/* Heap Header pointer */
register char		*pb;		/* Block pointer */
{
  int			c;		/* Counter */
  ulong_t		cmodscur;	/* Current modification count */
  register VMKRHY	*pkrhy; 	/* Yield array pointer */

  pkrhy = pkrh->krh_akrhy;		/* Initialize pointer */
  if(vmHasYieldedKRHB(pb))		/* If someone has yielded here */
    {
      for(c = krhb_MAXYIELD; c-- > 0; ++pkrhy)
	{				/* Loop to find match */
	  if(pkrhy->krhy_pb == pb) break;
					/* Break when match found */
	}
      vmAssert(c != 0); 		/* Must have found match */
      ++pkrhy->krhy_cyield;		/* Increment count */
    }
  else					/* Else no yield here yet */
    {
      for(c = krhb_MAXYIELD; c-- > 0; ++pkrhy)
	{				/* Loop to find match */
	  if(pkrhy->krhy_pb == NULL)	/* If empty slot found */
	    {
	      pkrhy->krhy_pb = pb;	/* Set pointer */
	      pkrhy->krhy_cyield = 1;	/* Set count to 1 */
	      vmSetYieldKRHB(pkrh, pb); /* Set yield bit in header */
	      break;			/* Exit loop */
	    }
	}
    }
  cmodscur = pkrh->krh_cMods;		/* Get current modification count */
  TKResched();				/* Yield the CPU */
  if(c != 0)				/* If we had a slot */
    {
      vmAssert(pkrhy->krhy_cyield != 0 && pkrhy->krhy_pb != NULL);
					/* Must have valid entry */
      pb = pkrhy->krhy_pb;		/* Set pointer */
      vmAssert(vmHasYieldedKRHB(pb));	/* Bit must be set in header */
      if(--pkrhy->krhy_cyield == 0)	/* If count goes to zero */
	{
	  pkrhy->krhy_pb = NULL;	/* Mark table entry as free */
	  vmClrYieldKRHB(pkrh, pb);	/* Clear the yield bit */
	}
    }
  else if(cmodscur != pkrh->krh_cMods)
    {
      pb = PKRHBNULL;
    }
					/* Else restart if heap has changed */
  return(pb);				/* Return point at which to resume */
}


/***LP	vmCoalesceKRHB - Coalesce block with neighboring free blocks
 *
 *	Given a pointer to a newly freed block (not yet on the free list),
 *	or to an existing non-free block, this function coalesces the block
 *	with the consecutive block and/or preceding block if they are free and
 *	have not been yielded at.  If the resulting block has the free
 *	owner, it is placed on the appropriate free list.
 *
 *	void vmCoalesceKRHB(VMKRH *pkrh, char *pb, ulong_t fl)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to free block
 *			fl		- Flags
 *					    VMS_DECOMMIT - decommit
 *					      internal free pages
 *	RETURN		void		- Any error is an internal error
 *
 *	CALLS		vmNextKRHB, vmSizeKRHB, vmIsFreeKRHB, vmHasYieldedKRHB,
 *			vmSizeRegKRHB, vmSizeLargeKRHB, vmSetSizeKRHB,
 *			vmRemoveFreeKRHB
 *
 *	EFFECTS 	Changes the freelist
 */

void vmCoalesceKRHB(pkrh, pb, fl)
register VMKRH		*pkrh;		/* Pointer to heap header */
register char		*pb;		/* Pointer to free block */
ulong_t 		fl;		/* Flags */
{
  register char 	*pbNext;	/* Next block pointer */
  char			*pbPrev;	/* Previous block pointer */
  char			*pbBack;	/* Previous block back pointer */
  char			*pbDecommitMin;
  char			*pbDecommitMax;
  char			*pbNextDecommitMin;
  char			*pbNextDecommitMax;
  char			*pbPrevDecommitMin;
  char			*pbPrevDecommitMax;
  ulong_t		cbPrev; 	/* Size of preceding free block */
  ulong_t		cbNext; 	/* Size of succeeding free block */
  ulong_t		cbNew;		/* New size of block */
  ulong_t		cbOld;		/* Original size of block */
  ulong_t		rc;		/* Callee result code */

  cbOld = vmSizeKRHB(pb);
  cbNew = cbOld;
  cbNext = cbPrev = 0;

  /*
   *  If the succeeding block is free, disconnect it from the free list and
   *  add in its size.
   */

  pbNext = vmNextKRHB(pb);
  if(vmIsFreeKRHB(pbNext) && !vmHasYieldedKRHB(pbNext))
    {
      /*
       *  This block is being coalesced.  Disconnect it from free list.  Add
       *  in size to total size accumulated so far.
       */

      cbNext = vmSizeKRHB(pbNext);
      vmRemoveFreeKRHB(pkrh, pbNext);
      cbNew += cbNext;

      /*
       *  The next again block should not be free unless someone has yielded
       *  at it.  Verify this.
       */

      vmAssert((!vmIsFreeKRHB(vmNextKRHB(pbNext))) ||
		vmHasYieldedKRHB(vmNextKRHB(pbNext)));
    }

  /*
   *  If the current block is free, look at preceding block.  A flag in the
   *  header of the current block indicates whether the preceding block is
   *  free.  If this is the case, the preceding block's last ulong contains
   *  the size of the block, unless the block is sizeof(VMKRHF) bytes long,
   *  in which case a flag is set.
   */

  if((!vmIsFreeKRHB(pb)) || !vmHasPrevFreeKRHB(pb))
    {
      /*
       *  The current or preceding block is not free so we will not coalesce
       *  with the preceding block.  Set the new block size in
       *  the current block and insert it into the free list if it
       *  has the free owner.
       */

      vmAssert(vmIsFreeKRHB(pb) || !(fl & VMS_DECOMMIT));
      vmGrowKRHB(pkrh, pb, cbNew);
      if(vmIsFreeKRHB(pb))
	{
	  vmInsertFreeKRHB(pkrh, pb);
	}
    }

  else
    {
      /*
       *  The preceding block is free.	Get pointer to it and add in its
       *  size.  There are 4 cases controlled by the setting of bits
       *  0 (krhb_ATTR) and 1 (krhb_PBBACK).
       *
       *  (1)  krhb_PBBACK clear, krhb_ATTR clear
       *       Preceding block is regular of size sizeof(VMKRHF).  There
       *       is no back pointer.
       *  (2)  krhb_PBBACK clear, krhb_ATTR set.
       *       Preceding block is attributed of size sizeof(VMKRHF)+
       *       sizeof(VMKHBA).	There is no back pointer.
       *  (3)  krhb_PBBACK set, krhb_ATTR clear
       *       Preceding block is regular.  Last ulong (ignoring bits 0,1)
       *       contains a back pointer to its header.
       *  (4)  krhb_PBBACK set, krhb_ATTR set
       *       Preceding block is attributed and its end contains a
       *       VMKHBA structure preceded by a ulong containing a back
       *       pointer to its header.  Note that the khba_sel field
       *       occupies bits 0 thru 15 of the final ulong, so krhb_PBACK
       *       and krhb_ATTR occupy the same positions as the RPL bits.
       *       This is OK, since a free attributed block does not have
       *       a selector, so the selector field is not needed.
       */

      pbBack = pb - sizeof(char *);

      /*
       *  If there is a back pointer to the previous block, load it.
       */

      if((*((ulong_t *) pbBack)) & krhb_PBBACK)
	{
	  if((*((ulong_t *) pbBack)) & krhb_ATTR)
	    {
	      pbBack -= sizeof(VMKHBA);
	    }
	  pbPrev =
	    (char *)((*((ulong_t *) pbBack)) & ~(krhb_ATTR | krhb_PBBACK));
	}

      /*
       *  Else there is no back pointer.  Calculate address of previous block.
       */

      else
	{
	  if((*((ulong_t *) pbBack)) & krhb_ATTR)
	    {
	      pbPrev = pb - sizeof(VMKHBA) - sizeof(VMKRHF);
	    }
	  else
	    {
	      pbPrev = pb - sizeof(VMKRHF);
	    }
	}

      /*
       *  Verify that the back pointer points within the heap, references
       *  a block header and references a free block.
       */

      vmAssert(vmIsWithinKRH(pkrh, pbPrev));
      vmAssert(vmIsValidKRHB(pbPrev));
      vmAssert(vmIsFreeKRHB(pbPrev));

      cbPrev = vmSizeKRHB(pbPrev);
      cbNew += cbPrev;

      /*
       *  Now remove the previous free block from its free list and create the
       *  new coalesced block on top.  The coalesced block will be a
       *  regular block if it is small enough.
       */

      vmRemoveFreeKRHB(pkrh, pbPrev);
      vmCreateKRHB(pkrh, pbPrev, cbNew, pkhbaFree, NA);
    }

  /*
   *  If to decommit pages, initialize decommit pointers
   */

  if(fl & VMS_DECOMMIT)
    {
      pbDecommitMin = vmDecommitMinKRHB(pb);
      pbDecommitMax = vmDecommitMaxKRHB(pb, cbOld);

      /*
       *  If the succeeding block was coalesced, see if we need to
       *  decommit the page containing its header.
       */

      if(cbNext > 0)
	{
	  /*
	   *  Get the bounds of the interior decommitted region of the
	   *  next block.  Set the end of the region to be
	   *  decommitted now equal to the start of next block's
	   *  decommitted region, or, if none, to the start of the first
	   *  of the page(s) containing the next block's header/free list
	   *  links.
	   */

	  pbNextDecommitMin = vmDecommitMinKRHB(pbNext);
	  pbNextDecommitMax = vmDecommitMaxKRHB(pbNext, cbNext);
	  pbDecommitMax = (pbNextDecommitMin < pbNextDecommitMax) ?
	    pbNextDecommitMin : pbNextDecommitMax;
	}

      /*
       *  If the preceding block was coalesced, see if we need to
       *  decommit the page containing its back pointer/attribute block
       */

      if(cbPrev > 0)
	{
	  /*
	   *  Get the bounds of the interior decommitted region of the
	   *  preceding block.	Set the start of the region to be
	   *  decommitted now equal to the end of the previously
	   *  decommitted region, or, if none, to the end of the page(s)
	   *  containing the preceding block's header/free list links.
	   */

	  pbPrevDecommitMin = vmDecommitMinKRHB(pbPrev);
	  pbPrevDecommitMax = vmDecommitMaxKRHB(pbPrev, cbPrev);
	  pbDecommitMin = (pbPrevDecommitMax >	pbPrevDecommitMin) ?
	    pbPrevDecommitMax : pbPrevDecommitMin;
	}

      /*
       *  Decommit pages if any.
       */

      if(pbDecommitMin < pbDecommitMax)
	{
	  while((rc = PGFree(((ulong_t) pbDecommitMin) >> PAGESHIFT,
			      NULL,
			     (pbDecommitMax - pbDecommitMin) >> PAGESHIFT,
			     PG_CHKLOCK)) == ERROR_INTERRUPT);
	  vmAssert(rc == NO_ERROR);
	}
    }
}


/***LP	vmGrowKRHB - Increase size of a Kernel Resident Heap Block
 *
 *	This function increases the size of a Kernel Resident Heap Block.
 *	The block will be converted into an attributed block if
 *	necessary.  It is the responsibility of the caller to update the
 *	free list and to ensure that there is sufficient room for the
 *	expanded/converted block.
 *
 *	void vmGrowKRHB(VMKRH *pkrh, char *pb, ulong_t cbNew)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to block whose size
 *					  is to be changed.  If the block
 *					  is a large block, pb references
 *					  the size word, not the prefix block.
 *			cbNew		- New size of block
 *	RETURN		void
 *
 *	CALLS
 *	EFFECTS
 */

void vmGrowKRHB(pkrh, pb, cbNew)
register VMKRH		*pkrh;		/* Pointer to heap header */
register char		*pb;		/* Pointer to block header */
ulong_t 		cbNew;		/* New size of block */
{
  ulong_t		ulNewHdr;	/* New block header value */
  ulong_t		ulOldHdr;	/* Old block header value */
  VMKHBA		khba;		/* Attribute area */

  /*
   *  If the old and new blocks are regular, simply update the size
   *  in the header and return.
   */

  if(vmIsRegKRHB(pb) && (cbNew <= krhb_REG_MAX_SIZE))
    {
      ulNewHdr = (vmHdrKRHB(pb) & (~(krhb_REG_SIZE_MASK))) | cbNew;
      vmWriteULKRHB(pkrh, pb, ulNewHdr);
      return;
    }

  /*
   *  New block will be attributed.  Create header containing size and
   *  marhed with attributed flag; propagate the "previous block is free"
   *  flag noting that this is a different flag for attributed or regular
   *  blocks.  Set up or copy the attribute area.
   */

  ulNewHdr = cbNew | krhb_ATTR;
  ulOldHdr = vmHdrKRHB(pb);
  if(vmIsRegKRHB(pb))
    {
      khba.khba_hobOwner = vmOwnerRegKRHB(pb);
      khba.khba_sel = NA;
      if(ulOldHdr & krhb_PFREE)
	{
	  ulNewHdr |= krhb_ATTR_PFREE;
	}
    }
  else
    {
      /*
       *  BUGBUG IDB 9/26/90 The following statement trashes flat ES via
       *  push ss, pop es.  It is temporarily replaced with the
       *  vmCopyAttrKRHB macro call below it untile the C compiler is fixed.
       *
       *  khba = *vmpkhbaKRHB(pb);
       */

      vmCopyAttrKRHB(khba, *(vmpkhbaKRHB(pb)));
      if(ulOldHdr & krhb_ATTR_PFREE)
	{
	  ulNewHdr |= krhb_ATTR_PFREE;
	}
    }

  vmWriteULKRHB(pkrh, pb, ulNewHdr);
  vmSetAttrKRHB(pkrh, pb, SSToDS(&khba));
}


/***LP	vmCarveKRHB - Carve Kernel Resident Heap Block
 *
 *	Given a pointer to a free block and a size, or to an uninitialized
 *	portion of heap memory, this function carves the block or region into
 *	a block of the given size and (if the free block or region is large
 *	enough) a residual free block.	The first block is either allocated
 *	or placed on the appropriate free list depending on the flags.	The
 *	residual free block (if any) is placed on the appropriate free list.
 *
 *	void vmCarveKRHB(VMKRH *pkrh,
 *			 char *pb,
 *			 ulong_t cbFirst,
 *			 ulong_t cbTotal,
 *			 VMKHBA *pkhba,
 *			 ulong_t flCarve)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to free block or region
 *			cbFirst 	- Size of first block (including hdr)
 *			cbTotal 	- Total size of region.  This is not
 *					  necessarily related to the size of
 *					  an existing block.
 *			pkhba		- First sub-block's attributes
 *					  (hobOwner, sel)
 *			flCarve 	- Flags
 *					    krhb_CR_NEW - Block is new
 *					    krhb_ATTR - Block is attributed
 *					      regardless of size
 *					    krhb_CR_NOINSFR - Residual
 *					      free block not to be added to
 *					      free list because it will be
 *					      carved again.
 *
 *	RETURN		char *		- Pointer to header of first block.
 *					  This value equals pb except when
 *					  carving a regular block from a
 *					  large free block.
 *
 *	CALLS		vmCreateKRHB
 *
 *	EFFECTS 	Changes the freelist(s)
 */

void vmCarveKRHB(pkrh, pb, cbFirst, cbTotal, pkhba, flCarve)
VMKRH			*pkrh;		/* Pointer to heap header */
register char		*pb;		/* Pointer to free block or region */
register ulong_t	cbFirst;	/* Size of first block */
register ulong_t	cbTotal;	/* Total size of block or region */
VMKHBA			*pkhba; 	/* First sub-block's attributes */
ulong_t 		flCarve;	/* flags */
{
  ulong_t		cbResid;	/* Size of residual free block */

  /*
   *  Validate flags.  Only permitted flags are krhb_CR_NEW, krhb_ATTR and
   *  krhb_CR_NOINSFR.
   */

  vmAssert(flCarve == (flCarve & (krhb_CR_NEW |
				  krhb_ATTR |
				  krhb_CR_USEDECO |
				  krhb_CR_NOINSFR)));

  /*
   *  Check if there will be enough space for a residual free block.
   *  If so, allocate residual block.  If not, change request size to
   *  be whole size available.
   */

  if((cbResid = cbTotal - cbFirst) >= krhb_REG_MIN_SIZE)
    {
      /*
       *  There is enough block left to form a residual free block. Carve
       *  out residual free block and add it to the free list unless
       *  requested not to do so.
       */

      vmCreateKRHB(pkrh, pb + cbFirst, cbResid, pkhbaFree,
		   krhb_CR_NEW | krhb_CR_NXVAL | (flCarve & krhb_CR_NOINSFR));
    }
  else
    {
      cbFirst = cbTotal;
    }

  /*
   *  Set the size of the first sub-block, mark as allocated to the given
   *  owner.  If the block is to be free, add it to the free list.
   */

  vmCreateKRHB(pkrh,pb,cbFirst,pkhba,
		(flCarve & ~krhb_CR_NOINSFR) | krhb_CR_NXVAL | krhb_CR_NOMERGE);
}


/***LP	vmRelinkFreeKRHB - Move free block to correct free list.
 *
 *	This function checks a free block to determine whether it is
 *	on the correct free list.  If it is not, the block is moved to
 *	the correct free list.
 *
 *	void vmRelinkFreeKRHB(VMKRH *pkrh, char *pb, ulong_t cbOld)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to free block
 *			cbOld		- Original block size
 *	RETURN		void		- Any error is an Internal Error
 *
 *	CALLS		vmSizeToPKRHS, vmSizeKRHB, vmIsLargeKRHB,
 *			vmInsertFreeKRHB, vmRemoveFreeKRHB
 *
 *	EFFECTS 	Changes the freelist(s)
 */

void vmRelinkFreeKRHB(pkrh, pb, cbOld)
register VMKRH		*pkrh;		/* Pointer to heap header */
register char		*pb;		/* Pointer to free block */
register ulong_t	cbOld;		/* Original Block Size */
{
  VMKRHS		*pkrhs; 	/* Pointer to new free list header */

  /*
   *  Compare correct free list pointer with current.  If different, unhook
   *  block from old free list and place at head of new free list.
   */

  if((pkrhs = vmSizeToPKRHS(pkrh, vmSizeKRHB(pb))) != vmSizeToPKRHS(pkrh,cbOld))
    {
      /*
       *  Block either not on any freelist or on the wrong free list.
       *  Unhook it (if necessary) from old free list and insert it on
       *  the correct free list.
       */

      if(pkrhs != PKRHSNULL)
	{
	  vmRemoveFreeKRHB(pkrh, pb);
	}
      vmInsertFreeKRHB(pkrh, pb);
    }
}


/***LP	vmInsertFreeKRHB - Put free block on free list
 *
 *	Given a pointer to a free block, this function adds the block
 *	to the head of the appropriate freelist section.  Pointers in
 *	preceding freelist section headers are updated if they referenced
 *	the same block as the current section header.  The succeeding block
 *	is marked with the flag krhb_PFREE to indicate that the preceding
 *	block is free.	If the block being freed has total size > the minimum
 *	size of a free block, a pointer to the block header is stored in
 *	the last ulong to enable backward coalescing.
 *
 *	void vmInsertFreeKRHB(VMKRH *pkrh, char *pb)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to free block header
 *	RETURN		void		- Function internal errors if error
 *
 *	CALLS		vmSizeKRHB, vmSizeToPKRHS, vmWriteULKRHB
 *
 *	EFFECTS 	Changes the freelist.
 */

void vmInsertFreeKRHB(pkrh, pb)
register VMKRH		*pkrh;		/* Pointer to heap header */
register char		*pb;		/* Pointer to free heap block header */
{
  VMKRHS		*pkrhs; 	/* Pointer to free list section hdr */
  char			*pbOldHead;	/* Pointer to old free list head */
  char			*pbNext;	/* Pointer to next block */
  char			*pbBack;	/* Back pointer pointer */
  char			*pbLastUL;	/* Pointer to last ulong_t in block */
  ulong_t		cbMin;		/* Minimum size for reg/attr block */
  ulong_t		flBack; 	/* Back pointer flags */
  ulong_t		flPFree;	/* Previous free bit */
  register ulong_t	cb = vmSizeKRHB(pb);

  /*
   *  Find the free list section to which the block is to be prepended.
   */

  pkrhs = vmSizeToPKRHS(pkrh, cb);
  pbOldHead = vmHeadFreeKRHB(pkrhs);

  /*
   *  Link the block onto the free list just before the head block of the
   *  section.	Optimize the case where the heap is writable.
   */

  if(pkrh->krh_fl & PG_W)
    {
      vmNextFreeKRHB(pb) = pbOldHead;
      vmPrevFreeKRHB(pb) = vmPrevFreeKRHB(pbOldHead);
      vmNextFreeKRHB(vmPrevFreeKRHB(pb)) = pb;
      vmPrevFreeKRHB(vmNextFreeKRHB(pb)) = pb;
    }
  else
    {
      vmWriteULKRHB(pkrh,&(vmNextFreeKRHB(pb)),(ulong_t)(pbOldHead));
      vmWriteULKRHB(pkrh,&(vmPrevFreeKRHB(pb)),
			(ulong_t) vmPrevFreeKRHB(pbOldHead));
      vmWriteULKRHB(pkrh,&(vmNextFreeKRHB(vmPrevFreeKRHB(pb))), (ulong_t) pb);
      vmWriteULKRHB(pkrh,&(vmPrevFreeKRHB(vmNextFreeKRHB(pb))), (ulong_t) pb);
    }

  /*
   *  Update free list section head pointer for this section and all preceding
   *  "empty" sections whose head pointers reference the original head block
   *  of this section.
   */

  while(vmHeadFreeKRHB(pkrhs) == pbOldHead)
    {
      vmHeadFreeKRHB(pkrhs) = pb;
      pkrhs--;
    }

  /*
   *  Mark the succeeding block with the krhb_PFREE flag (if regular) or
   *  krhb_ATTR_PFREE flag (if attributed).  If the free block is regular
   *  with size > krhb_REG_MIN_SIZE store a back pointer to its header in the
   *  last ulong and set the krhb_PBBACK bit there.  If the free block is
   *  attributed with size > krhb_ATTR_MIN_SIZE store a back pointer to its
   *  header in the last ulong preceding the attribute area at the end of the
   *  block.  Set the krhb_PBBACK bit there.  The krhb_PBBACK bit enables a
   *  backward coalesce operation to distinguish a back pointer from a backward
   *  free list pointer.
   */

  pbNext = vmNextKRHB(pb);
  pbLastUL = pbNext - sizeof(char *);
  pbBack = pbLastUL;
  flBack = krhb_PBBACK;
  flPFree = (vmIsRegKRHB(pbNext)) ? krhb_PFREE : krhb_ATTR_PFREE;
  cbMin = krhb_REG_MIN_SIZE;
  if(vmIsAttrKRHB(pb))
    {
      pbBack -= sizeof(VMKHBA);
      cbMin = krhb_ATTR_MIN_SIZE;
      flBack = (cb > cbMin) ? (krhb_PBBACK | krhb_ATTR) : krhb_ATTR;

      /*
       *  Additionally, set the flags in the last ulong, taking care
       *  not to disturb the rest of this ulong (= end ulong of attribute
       *  area).
       */

      vmWriteULKRHB(pkrh,
		    pbLastUL,
		    (((*((ulong_t *) pbLastUL))
		      & ~(krhb_ATTR | krhb_PBBACK)) | flBack));
    }

  /*
   *  If the block size is not the minimum of its type, set the back
   *  pointer and flags.
   */

  if(cb > cbMin)
    {
      vmWriteULKRHB(pkrh, pbBack, (((ulong_t) pb) | flBack));
    }

  vmWriteULKRHB(pkrh, pbNext, (vmHdrKRHB(pbNext) | flPFree));
}


/***LP	vmRemoveFreeKRHB - Remove block from free list
 *
 *	This function unlinks a block from the free list.  The free list
 *	section head pointer for the block is updated to point to the next
 *	block.	Preceding "empty" free list section head pointers referencing
 *	the block being removed are similarly updated.
 *
 *	void vmRemoveFreeKRHB(VMKRH *pkrh, char *pb)
 *
 *	ENTRY		*pkrh		- Pointer to heap header
 *			pb		- Pointer to free block header.  The
 *					  block must be linked into a free list.
 *
 *	RETURN		void		- Any error is an Internal Error
 *
 *	CALLS		vmIsFreeKRHB, vmWriteULKRHB, vmNextFreeKRHB,
 *			vmPrevFreeKRHB, vmIsEmptyKRHS
 *
 *	EFFECTS 	Changes freelist(s).  Updates freelist selector
 *			hint pointer.
 *
 *	WARNING 	Do not call this function to allocate a block, call
 *			vmCarveKRHB instead.
 */

void vmRemoveFreeKRHB(pkrh, pb)
register VMKRH		*pkrh;		/* Pointer to heap header */
register char		*pb;		/* Pointer to block on free list */
{
  char			*pbNewHead;	/* Pointer to new head block */
  char			*pbNext;	/* Pointer to next block */
  char			*pbPrev;	/* Successor's prev. free block */
  char			*pbBack;	/* Prev. free block back pointer */
  VMKRHS		*pkrhs; 	/* Pointer to free list section hdr */
  ulong_t		ulPFreeBit;	/* krhb_PFREE, krhb_ATTR_PFREE or 0 */
  ulong_t		ulHdrNext;	/* Next block's header */

  /*
   *  Verify that the block is free.  Get succeeding block.
   */

  vmAssert(vmIsFreeKRHB(pb));
  pbNewHead = vmNextFreeKRHB(pb);

  /*
   *  Unhook the block from its doubly-linked free list.  Note that the free
   *  list never empties because it always contains a dummy block (embedded
   *  in the heap header).
   */

  vmWriteULKRHB(pkrh,
		&(vmPrevFreeKRHB(vmNextFreeKRHB(pb))),
		(ulong_t) vmPrevFreeKRHB(pb));
  vmWriteULKRHB(pkrh,
		&(vmNextFreeKRHB(vmPrevFreeKRHB(pb))),
		(ulong_t) vmNextFreeKRHB(pb));

  /*
   *  If the succeeding block has the krhb_PFREE or krhb_ATTR_PFREE flag set
   *  to indicate that the preceding block is free, and there is a back
   *  pointer to the present block, clear the flag.  Note that since
   *  carving is done backwards, the address stored there may be the
   *  address of a newly-formed residual block and so may differ from
   *  the present block.
   */

  pbNext = vmNextKRHB(pb);
  ulHdrNext = vmHdrKRHB(pbNext);
  ulPFreeBit = 0;
  if(vmIsRegKRHB(pbNext) && vmHasPrevFreeRegKRHB(pbNext))
    {
      ulPFreeBit = krhb_PFREE;
    }
  else if(vmIsAttrKRHB(pbNext) && vmHasPrevFreeAttrKRHB(pbNext))
    {
      ulPFreeBit = krhb_ATTR_PFREE;
    }

  if(ulPFreeBit != 0)
    {
      pbBack = pbNext - sizeof(char *);
      pbPrev = (char *)(*((ulong_t *) pbBack));
      if(((ulong_t) pbPrev) & krhb_PBBACK)
	{
	  if(((ulong_t) pbPrev) & krhb_ATTR)
	    {
	      pbBack -= sizeof(VMKHBA);
	    }
	  pbPrev = (char *)(*((ulong_t *) pbBack) & ~(krhb_PBBACK | krhb_ATTR));
	  if(pbPrev != pb)
	    {
	      ulPFreeBit = 0;
	    }
	}

      /*
       *  Clear the previous block free bit if necessary.
       */

      if(ulPFreeBit != 0)
	{
	  vmWriteULKRHB(pkrh, pbNext, ulHdrNext & ~ulPFreeBit);
	}
    }

  /*
   *  Update free list section head pointers that reference the block.
   */

  pkrhs = vmSizeToPKRHS(pkrh, vmSizeKRHB(pb));
  while(vmHeadFreeKRHB(pkrhs) == pb)
    {
      vmWriteULKRHB(pkrh, &(vmHeadFreeKRHB(pkrhs)), (ulong_t) pbNewHead);
      pkrhs--;
    }
}

/***LP	vmCommitSubKRHB - Commit Pages for SubBlock of KRHB
 */

int vmCommitSubKRHB(pkrh, pb, cbRound, cbAvail, fl)
VMKRH			*pkrh;
char			*pb;
ulong_t 		cbRound;
ulong_t 		cbAvail;
ulong_t 		fl;
{
  char			*pbCommitSub;
  char			*pbCommitSubLim;
  char			*pbCommitEncLim;
  ulong_t		lpgBaseCommit;		/* Base page to commit */
  ulong_t		rc = NO_ERROR;

  /*
   *  Get region of pages to commit (if any).  The start of the
   *  region is the first page following the page(s) that overlap with
   *  the block header+free list link words.  The end of the region
   *  is the end of the page(s) that will contain the residual
   *  block, clamped to the end of the decommitted region of the
   *  original block.
   */

  pbCommitSub = (char *)(vmPageCeil(pb + sizeof(VMKRHF)));
  pbCommitSubLim = (char *)(vmPageCeil(pb + cbRound + sizeof(VMKRHF)));
  pbCommitEncLim = (char *)(vmPageFloor(pb + cbAvail - sizeof(VMKHBA)));
  if(pbCommitSubLim > pbCommitEncLim)
    {
      pbCommitSubLim = pbCommitEncLim;
    }

  if(pbCommitSub < pbCommitSubLim)
    {
      lpgBaseCommit = ((ulong_t) pbCommitSub) >> PAGESHIFT;

      rc = PGAlloc(((ulong_t) pbCommitSub) >> PAGESHIFT,
		       NULL,
		       (pbCommitSubLim - pbCommitSub) >> PAGESHIFT,
		       NA,
		       pkrh->krh_hob,
		       lpgBaseCommit - pkrh->krh_lpgBase,
		       fl | pkrh->krh_fl);
    }

  vmAssert(rc != ERROR_ACCESS_DENIED);
  return(rc);
}


/***LP	vmCommitSubAllocKRHB - Commit pages for subblock being allocated
 *	from free block.
 *
 *	Given a pointer to a free block, a pointer to a sub-block to be
 *	allocated and a pointer to a residual free region following the
 *	allocated block, this function commits any pages needed for the
 *	allocated block.  If the allocated block will have preceding free
 *	or residual free blocks, up to 2 additional pages may also have to
 *	committed if the attribute area of the preceding block or the
 *	header + free links for the succeeding free block do not have
 *	pages committed.
 *
 *	Internal pages of the given free block are assumed to be already
 *	decommitted.
 *
 *	int vmCommitSubAllocKRHB(VMKRH *pkrh,
 *				 char *pb,
 *				 char *pbAlloc,
 *				 char *pbResid,
 *				 ulong_t cbAvail,
 *				 ulong_t fl)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to free block
 *			pbAlloc 	- Pointer to sub block to be allocated
 *			pbResid 	- Pointer to residual free block.  If
 *					  there is no free residual part, this
 *					  pointer = pb + cbAvail.
 *			cbAvail 	- Size of free block.
 *			fl		- Flags
 *					    Allocation flags specific to
 *					    request (see VMAllocKHB)
 */

int vmCommitSubAllocKRHB(pkrh, pb, pbAlloc, pbResid, cbAvail, fl)
VMKRH			*pkrh;
char			*pb;
char			*pbAlloc;
char			*pbResid;
ulong_t 		cbAvail;
ulong_t 		fl;
{
  char			*pbCommitMin;		/* Lower bound to commit */
  char			*pbCommitMax;		/* Upper bound to comit */
  char			*pbCommitOuterMin;	/* Orig block dec. lower bd */
  char			*pbCommitOuterMax;	/* Orig block dec. upper bd */
  ulong_t		rc;
  ulong_t		cbBefore = pbAlloc - pb;
  ulong_t		lpgBaseCommit;		/* Base page to commit */

  /*
   *  The internal pages of the block defined by pb, cbAvail are decommitted.
   *  Get the lower and upper bounds of the decommitted region of the whole
   *  block.
   */

  pbCommitOuterMin = vmDecommitMinKRHB(pb);
  pbCommitOuterMax = vmDecommitMaxKRHB(pb, cbAvail);

  /*
   *  Now get the lower and upper bounds of the region containing the
   *  block to be allocated.
   */

  pbCommitMin = vmDecommitMaxKRHB(pb, cbBefore);
  pbCommitMax = vmDecommitMinKRHB(pbResid);

  /*
   *  Clamp the region to be committed to exclude already committed pages
   *  at the beginning and end of the original block.
   */

  if(pbCommitMin < pbCommitOuterMin)
    {
      pbCommitMin = pbCommitOuterMin;
    }
  if(pbCommitOuterMax < pbCommitMax)
    {
      pbCommitMax = pbCommitOuterMax;
    }

  /*
   *  Commit any needed pages.
   */

  if(pbCommitMin < pbCommitMax)
    {
      lpgBaseCommit = ((ulong_t) pbCommitMin) >> PAGESHIFT;

      if((rc = PGAlloc(((ulong_t) pbCommitMin) >> PAGESHIFT,
		       NULL,
		       (pbCommitMax - pbCommitMin) >> PAGESHIFT,
		       NA,
		       pkrh->krh_hob,
		       lpgBaseCommit - pkrh->krh_lpgBase,
		       fl | pkrh->krh_fl)) != NO_ERROR);
	{
	  vmAssert(rc != ERROR_ACCESS_DENIED);
	  return(rc);
	}
    }
  return(NO_ERROR);
}



/***LP	vmDecommitKRHB - Decommit Internal Pages of Kernel Resident Heap Block
 *
 *	This function commits all of the interior pages (if any) of a kernel
 *	resident heap block.  The page(s) containing the header followed by the
 *	free list forward and back links will not be decommitted.  Similarly,
 *	the page(s) containing the back pointer or attribute area at the end
 *	end of the block are always retained.
 *
 *	void vmDecommitKRHB(char *pb)
 *
 *	ENTRY		pb			- Pointer to heap block header
 */

void vmDecommitKRHB(pb)
char			*pb;			/* Pointer to heap block */
{
  char			*pbDecommitMin;
  char			*pbDecommitMax;
  ulong_t		cbRound = vmSizeKRHB(pb);
  ulong_t		rc;

  /*
   *  Find the beginning and end of the region (if any) to decommit.  Allow
   *  enough space at the beginning of the block for the header and free
   *  list links; allow enough space for an attribute area or back link
   *  at the end of the block.
   */

   pbDecommitMin = vmDecommitMinKRHB(pb);
   pbDecommitMax = vmDecommitMaxKRHB(pb, cbRound);
   if(pbDecommitMin < pbDecommitMax)
     {
       while((rc = PGFree(((ulong_t) pbDecommitMin) >> PAGESHIFT,
			      NULL,
			     (pbDecommitMax - pbDecommitMin) >> PAGESHIFT,
			     PG_CHKLOCK)) == ERROR_INTERRUPT);
       vmAssert(rc == NO_ERROR);
     }
}


/***LP	vmFirstPKRHS - Find first free list to search for block of given size
 *
 *	Given a total blocksize in bytes, this function returns a pointer to
 *	the header of the first non-empty free list on which a block of at
 *	least that size could be contained.  If no such free list exists,
 *	a pointer to the dummy end free list is returned.
 *
 *	VMKRHS *vmFirstPKRHS(VMKRH *pkrh, ulong_t cb)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			cb		- Block size
 *	RETURN		VMKRHS *	- Pointer to free list
 *
 *	CALLS		vmIsEmptyKRHS, vmBlockSizeKRHS
 *
 *	EFFECTS 	NONE
 */

VMKRHS *vmFirstPKRHS(pkrh, cb)
register VMKRH		*pkrh;		/* Pointer to heap header */
register ulong_t	cb;		/* Block size */
{
  register VMKRHS	*pkrhs; 	/* Free list header pointer */

  /*
   *  Find first eligible non-empty free list header, returning null pointer
   *  if none.
   */

  for(pkrhs = &(pkrh->krh_akrhs[1]); vmBlockSizeKRHS(pkrhs) < cb; pkrhs++);
  return(pkrhs);
}


/***LP	vmSizeToPKRHS - Find free list header for given block size
 *
 *	Given a total blocksize in bytes, this function returns the pointer to
 *	the the header of the free list on which a free block of the given
 *	size would be placed.
 *
 *	VMKRHS *vmSizeToPKRHS(VMKRH *pkrh, ulong cb)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			cb		- Total blocksize
 *	RETURN		VMKRHS *	- Pointer to free list header
 *
 *	CALLS		NONE
 *
 *	EFFECTS 	NONE
 */

VMKRHS *vmSizeToPKRHS(pkrh, cb)
register VMKRH		*pkrh;		/* Pointer to heap header */
register ulong_t	cb;		/* Total blocksize */
{
  register VMKRHS	*pkrhs; 	/* Pointer to free list header */

  /*
   *  Scan the freelist section header array to find the freelist section
   *  to which a block of the given size belongs.  To prevent overrun of
   *  the scan loop, verify that cb <= max block size for the last section.
   *  Return the free list section header pointer to the caller.
   */

  vmAssert(cb <= pkrh->krh_akrhs[krh_CFREELIST - 1].krhs_cbMax);
  for(pkrhs = &pkrh->krh_akrhs[1]; cb > pkrhs->krhs_cbMax; pkrhs++);
  return(pkrhs);
}

/***LP	vmIsValidHKH - Validate Kernel Heap Header Handle
 *
 *	This function validates a given kernel heap header handle.  The handle
 *	must be in range and must correspond to a heap header.
 *
 *	vmIsValidHKH(hkh)
 *
 *	ENTRY	hkh		- Heap header handle
 *	RETURN			- TRUE if handle valid else FALSE
 *
 *	CALLS	NONE
 *
 *	EFFECTS NONE
 *
 *	WARNING This function is implemented as a macro.
 */

#define vmIsValidHKH(hkh) \
  (hkh >= hkh_MIN && hkh <= hkh_MAX && apkh[hkh].kh_pkrh != PKRHNULL)


/***LP	vmIsValidHKRH - Validate Kernel Resident Heap Header Handle
 *
 *	This function validates a given kernel resident heap header
 *	handle.  The handle must be in range and must correspond to
 *	a resident heap header.
 *
 *	vmIsValidHKRH(hkrh)
 *
 *	ENTRY	hkrh		- resident heap header handle
 *	RETURN			- TRUE if handle valid else FALSE
 *
 *	CALLS	NONE
 *
 *	EFFECTS NONE
 *
 *	WARNING This function is implemented as a macro.
 */

#define vmIsValidHKRH(hkh) \
  (hkh >= hkh_MIN && hkh <= hkrh_MAX && apkh[hkh].kh_pkrh != PKRHNULL)



#pragma code_seg("CINIT_TEXT", "CODE") /* This section goes to INIT code */

/***LP vmInitPublicHeaps - Initialize the Public Swappable and Resident Heaps
 *
 *	This function is called by VMInit to create the remaining five
 *	of the six public kernel resident and swappable heaps.	The first of
 *	these heaps has already been created by the time this function is
 *	called.  The six public heaps are as follows:
 *
 *	Public Resident Read Write Heap (accessed via alloc and free)
 *	Public Resident Read Only Heap
 *	Public Swappable Read Write Heap (accessed via salloc and sfree)
 *	Public Swappable Read Only Heap
 *	Public Resident Read Write Low Mem (<1M) heap
 *	Public Resident Read Only Low Mem (<1M) heap
 *
 *	ENTRY		None
 *	RETURN		Void.  Any error is an Internal Error
 *	EFFECTS 	Initializes the first two heap header pointers
 *			in each of the arrays apkrh[] and apksh[].
 */

void vmInitPublicHeaps(void)
{
  VMHKH 		hkh;		/* Heap handle */
  VMAC			ac;		/* VMAllocMem/VMInit info */
  int			rc;

  /*
   *  Create BMP32 object for holding Block Descriptor Records, for
   *  the public and all other swappable heaps.  It is an internal
   *  error if there is not enough resident memory to do this.
   *  Initialize the Public Kernel Resident and Swappable Read/Write
   *  and Read Only heaps.  Note that the Resident Read/Write heap MUST
   *  be initialized first, because all other heaps have headers allocated
   *  from it.
   */

  /* perf enh - if we have a constrained system (ie 4 meg), only use 2k of
   *   the 4k in the first page for the BMP structure and combine the sellock
   *   BMP package with this one
   */
// PERF-BUGBUG two constant below need defines
  if ( PGGlobalFlags & PG_SYS_CONSTRAINED ) {
      rc = BMPInit(0x5555,	 /* Maximum number of blocks */
#ifdef VMSTRICT
		 0xe,		 /* Size of a Block Descriptor */
#else
		 0xc,		 /* Size of a Block Descriptor */
#endif
				 // 0xe = sixeof sellockhandle
//    VMBMP_DONATEMEM | PG_RESIDENT,	/* Block Headers are resident */
			PG_RESIDENT,	/* Block Headers are resident */
		 VMKSHDOWNER,		/* Owned by swappable heap mgr */
		 NULL,			/* Default busy block id. fn */
		 &pkshdVMDescs);	/* Place to return pointer */
  } else {

     rc = BMPInit(vm_KSH_MAX_BLOCKS,	   /* Maximum number of blocks */
#ifdef VMSTRICT
		 0xe,		 /* Size of a Block Descriptor */
#else
//		 0xc,		 /* Size of a Block Descriptor */
		  sizeof(VMKSHD),	 /* Size of a Block Descriptor */
#endif
		  PG_RESIDENT,		   /* Block Headers are resident */
		  VMKSHDOWNER,		   /* Owned by swappable heap mgr */
		  NULL, 		   /* Default busy block id. fn */
		  &pkshdVMDescs);	   /* Place to return pointer */

  }
  vmAssert(rc == NO_ERROR);

#ifdef VMSTRICT
  rc = VMCreateKH(VM_MAX_RES_RO_HEAP_SIZE,
		   PG_RESIDENT | PG_R,
		   VMKRHROOWNER,
		   SSToDS(&hkh),
		   SSToDS(&ac));
  vmAssert(rc == NO_ERROR && hkh == (VMHKH) VM_HKH_PUB_RESRO);

  rc = VMCreateKH(VM_MAX_SWAP_RW_HEAP_SIZE,
		   PG_SWAPPABLE | PG_R | PG_W,
		   VMKSHRWOWNER,
		   SSToDS(&hkh),
		   SSToDS(&ac));
  vmAssert(rc == NO_ERROR && hkh == (VMHKH) VM_HKH_PUB_SWAPRW);

  rc = VMCreateKH(VM_MAX_SWAP_RO_HEAP_SIZE,
		   PG_SWAPPABLE | PG_R,
		   VMKSHROOWNER,
		   SSToDS(&hkh),
		   SSToDS(&ac));
  vmAssert(rc == NO_ERROR && hkh == (VMHKH) VM_HKH_PUB_SWAPRO);

  rc = VMCreateKH(VM_MAX_RES_RW_HEAP_SIZE,
		   PG_RESIDENT | PG_R | PG_W | PG_1M,
		   VMKRHRW1MOWNER,
		   SSToDS(&hkh),
		   SSToDS(&ac));
  vmAssert(rc == NO_ERROR && hkh == (VMHKH) VM_HKH_PUB_RES1MRW);

  rc = VMCreateKH(VM_MAX_RES_RO_HEAP_SIZE,
		   PG_RESIDENT | PG_R | PG_1M,
		   VMKRHRO1MOWNER,
		   SSToDS(&hkh),
		   SSToDS(&ac));
  vmAssert(rc == NO_ERROR && hkh == (VMHKH) VM_HKH_PUB_RES1MRO);
#else

  apkh[VM_HKH_PUB_RESRO].kh_fl	 = apkh[VM_HKH_PUB_RESRW].kh_fl;
  apkh[VM_HKH_PUB_RESRO].kh_pkrh = apkh[VM_HKH_PUB_RESRW].kh_pkrh;

  rc = VMCreateKH(VM_MAX_SWAP_RW_HEAP_SIZE+VM_MAX_SWAP_RO_HEAP_SIZE,
		   PG_SWAPPABLE | PG_R | PG_W,
		   VMKSHRWOWNER,
		   SSToDS(&hkh),
		   SSToDS(&ac));
  vmAssert(rc == NO_ERROR && hkh == (VMHKH) VM_HKH_PUB_SWAPRW);

  apkh[VM_HKH_PUB_SWAPRO].kh_pkrh = apkh[VM_HKH_PUB_SWAPRW].kh_pkrh;
  apkh[VM_HKH_PUB_SWAPRO].kh_fl   = apkh[VM_HKH_PUB_SWAPRW].kh_fl;

  apkh[VM_HKH_PUB_RES1MRW].kh_fl   = apkh[VM_HKH_PUB_RESRW].kh_fl;
  apkh[VM_HKH_PUB_RES1MRW].kh_pkrh = apkh[VM_HKH_PUB_RESRW].kh_pkrh;

  apkh[VM_HKH_PUB_RES1MRO].kh_fl   = apkh[VM_HKH_PUB_RESRW].kh_fl;
  apkh[VM_HKH_PUB_RES1MRO].kh_pkrh = apkh[VM_HKH_PUB_RESRW].kh_pkrh;

#endif
}


/***EP	VMRemoveUserAccessKHB - Remove user level access from the heap object
 *
 *	VMRemoveUserAccessKHB removes user level access from heap objects by
 *	turning off their PG_U flags.
 *
 *	void  VMRemoveUserAccessKHB(VMHKH hkh, char *pbData)
 *
 *	ENTRY		hkh		- Heap Handle
 *			pbData		- Address of heap object's data
 *	RETURN		NONE
 */
void	VMRemoveUserAccessKHB(hkh, pbData)
VMHKH			hkh;		/* Heap handle */
char			*pbData;	/* Address of heap block's data */
{
  int			rc;		/* Result code from callees */
  char			*pb;		/* Pointer to block's header */
  ulong_t		cbData; 	/* Size of heap block's data */

  if(vmIsHKRH(hkh))
    {
      /*
       *  Block belongs to a resident heap.  Get pointer to header, verify
       *  that block is valid and get size of data (excluding header).
       */

      pb = pbData - sizeof(VMKRHB);
      vmAssert(vmIsValidKRHB(pb));
      cbData = vmSizeDataKRHB(pb);
    }
  else
    {
      /*
       *  Block must belong to a swappable heap (internal error if not).  Get
       *  pointer to block's header, verify block's signature, and get data
       *  size (excluding header).
       */

//old	   pb = pbData - sizeof(VMKSHB);
//old	   cbData = kshSize(((VMKSHB *)pb)->kshb_pkshd);
      pb = pbData - sizeof(VMKSHB);
      cbData = kshbSize((VMKSHB *)pb);
    }

  /*
   *  Set access permissions for all pages containing heap block's data,
   *  leaving out PG_U; must set read/write permissions based on heap's
   *  properties.
   */

  rc = PGSetAccess((ulong_t) pbData >> PAGESHIFT, pPTDACur,
	(((ulong_t) pbData & PAGEMASK) + cbData + PAGEMASK) >> PAGESHIFT,
	(hkh & VM_HKH_WRITABLE) ? PG_R | PG_W : PG_R );
  vmAssert(rc == NO_ERROR);
}

#pragma code_seg()		/* End of initcode section */


/***EP	VMSetReadOnlyUL - Set unsigned long in read only memory
 *
 *	This function stores a supplied unsigned long value into a location in
 *	read-only memory.  The target location MUST NOT cross a page boundary.
 *
 *	ENTRY		pul		- Pointer to unsigned long
 *			ul		- Value to be stored
 *
 *	RETURN		void
 *
 *	PSEUDO CODE
 *
 *
 *	Read the target location to ensure that the page is present.
 *	Call the Page Manager to create a temporary alias to the page
 *	boundary below the target with one of the pages set aside for
 *	this purpose.  Use the returned alias virtual page number to
 *	access the target ulong by converting it to a virtual address,
 *	and adding in the page offset.
 */

void	VMSetReadOnlyUL(pul, ul)
ulong_t 		*pul;		/* Address of ulong to be written */
ulong_t 		ul;		/* Quantity to be stored */
{
  /*
   *  First touch the target page to make sure it is present.  Note
   *  the need to use the volatile keyword to prevent the compiler
   *  from optimizing this code out.  [#pragma optimize off does
   *  NOT achieve this effect!]
   */

  ulong_t	ulScratch = *((volatile ulong_t *)pul);

  /*
   *  Now write to target using temporary alias.
   */

  *(ulong_t *)(((PGMapTmpAlias((page_t)(pul) >> PAGESHIFT)) << PAGESHIFT)
	       + (((ulong_t)(pul)) & PAGEMASK)) = ul;
}


/***EP	VMSetReadOnlyUS - Set unsigned short in read only memory
 *
 *	This function stores a supplied ushort value into a location in
 *	read-only memory.  The target location MUST NOT cross a page
 *	boundary.
 *
 *	ENTRY		pus		- Pointer to unsigned short
 *			us		- Value to be stored
 *
 *	RETURN		void
 *
 *	PSEUDO CODE
 *
 *
 *	Read the target location to ensure that the page is present.
 *	Call the Page Manager to create a temporary alias to the page
 *	boundary below the target with one of the pages set aside for
 *	this purpose.  Use the returned alias virtual page number to
 *	access the target ulong by converting it to a virtual address,
 *	and adding in the page offset.
 */

void	VMSetReadOnlyUS(pus, us)
ushort_t		*pus;		/* Address of ushort to be written */
ushort_t		us;		/* Quantity to be stored */
{
  /*
   *  First touch the target page to make sure it is present.  Note
   *  the need to use the volatile keyword to prevent the compiler
   *  from optimizing this code out.  [#pragma optimize off does
   *  NOT achieve this effect!]
   */

  ushort_t		usScratch = *((volatile ushort_t *)pus);

  /*
   *  Now write to target using temporary alias.
   */

  *(ushort_t *)(((PGMapTmpAlias((page_t)(pus) >> PAGESHIFT)) << PAGESHIFT)
	       + (((ulong_t)(pus)) & PAGEMASK)) = us;
}



/***EP	VMAddReadOnlyULNew - Add to unsigned long in read only memory
 *
 *	This function adds a supplied unsigned long value into a location in
 *	read-only memory.  The target location MUST NOT cross a page
 *	boundary.
 *
 *	ENTRY		pul		- Pointer to unsigned long
 *			ul		- Value to be added
 *
 *	RETURN		void
 *
 *	PSEUDO CODE
 *
 *	Call the Page Manager to create a temporary alias to the page
 *	boundary below the target with one of the pages set aside for
 *	this purpose.  Use the returned alias virtual page number to
 *	access the target ulong by converting it to a virtual address,
 *	and adding in the page offset.
 */

void VMAddReadOnlyULNew(pul, ul)
ulong_t 		*pul;		/* Address of ulong to be added to */
ulong_t 		ul;		/* Quantity to be added in */
{
  /*
   *  First touch the target page to make sure it is present.  Note
   *  the need to use the volatile keyword to prevent the compiler
   *  from optimizing this code out.  [#pragma optimize off does
   *  NOT achieve this effect!]
   */

  ulong_t	ulScratch = *((volatile ulong_t *)pul);

  /*
   *  Now write to target using temporary alias.
   */

  *(ulong_t *)(((PGMapTmpAlias((page_t)(pul) >> PAGESHIFT)) << PAGESHIFT)
	       + (((ulong_t)(pul)) & PAGEMASK)) += ul;
}

#ifdef VMSTRICT

/***LP	vmIsValidKRH - Check integrity of resident heap
 *
 *	This function checks that all the linkages in a resident heap are
 *	consistent.  It is normally used only when debugging the heap and is
 *	only compled in when VM strict checking is enabled.
 *
 *	The heap is first walked as a singly-linked list of busy and free
 *	blocks.  The free list (doubly linked) is then walked forward and
 *	back.  Finally, the pointers into the free list corresponding to
 *	each block are checked.
 *
 *	int vmIsValidKRH(VMKRH *pkrh)
 *
 *	ENTRY		pkrh		- Pointer to resident heap header
 *	RETURN		int		TRUE if heap is valid, else FALSE
 *
 *	CALLS	vmIsValidHKRH, vmIsValidKRHB, vmSizeKRHB, VMNextKRHB
 */

int	vmIsValidKRH(pkrh)
register VMKRH		*pkrh;		/* Heap header pointer */
{
  register char 	*pb;		/* Block pointer */
  char			*pbPrev;	/* Previous block pointer */
  char			*pbPrevFree;	/* Previous free block pointer */
  char			*pbBack;	/* Back pointer pointer */
  char			*pbLastUL;	/* Last ulong pointer */
  VMKRHS		*pkrhs; 	/* Pointer to freelist section descr */
  ulong_t		ulLastUL;	/* Last ulong as a ulong */
  ulong_t		cbMax;		/* Max block size for freelist sect */
  ulong_t		cbPrev; 	/* Size of preceding block */
  ulong_t		cFreeConsec = 0; /* Count of free consectuive blocks */

  /*
   *  If run-time heap validation is disabled, return success.
   */

  if(!(vmkhGflags & VM_KH_HEAP_VAL))
    {
      return(TRUE);
    }

  pbPrev = NA;
  pkrh->krh_cBlocks = 0;
  pkrh->krh_cFreeBlocks = 0;
  for(pb = pkrh->krh_pbFirst; pb != pkrh->krh_pbLast; pbPrev = pb,
						      pb = vmNextKRHB(pb))
    {
      /*
       *  Count the blocks
       */

      pkrh->krh_cBlocks++;
      if(vmIsFreeKRHB(pb))
	{
	  pkrh->krh_cFreeBlocks++;
	  if(!vmHasYieldedKRHB(pb))
	    {
	      vmAssert(cFreeConsec == 0);
	      cFreeConsec++;
	    }
	  else
	    {
	      cFreeConsec = 0;
	    }
	}
      else
	{
	  cFreeConsec = 0;
	}

      if(!vmIsValidKRHB(pb))
	{
	  Panic(("Invalid heap block header at addr %x : %x\n",
	    pb, *((ulong_t *) pb)));
	  if(pbPrev != NA)
	    {
	      Panic(("Preceding block at addr = %x : %x\n",
	      pbPrev, *((ulong_t *) pbPrev)));
	    }
	  else
	    {
	      panic("No preceding block\n");
	    }
	  vmAssert(FALSE);
	}

      vmAssert(vmHasValidOwnerKRHB(pb));

      /*
       *  Check that two adjacent blocks are not free unless the yield bit is
       *  set in at least one of them.
       */

      vmAssert(pbPrev == NA ||
	       !(vmIsFreeKRHB(pbPrev) &&
		 (!vmHasYieldedKRHB(pbPrev)) &&
		 vmIsFreeKRHB(pb) &&
		 (!vmHasYieldedKRHB(pb))));

      /*
       *  If the preceding block is free, check that the preceding block
       *  is free bit is set.  If the preceding block is free bit is set
       *  check that the back pointer to it is set correctly (preceding
       *  free blocks of size > krhb_REG_MIN_SIZE only).
       */

      if(vmHasPrevFreeKRHB(pb))
	{
	  vmAssert(vmIsFreeKRHB(pbPrev));
	  cbPrev = vmSizeKRHB(pbPrev);
	  pbLastUL = pb - sizeof(char *);
	  ulLastUL = *((ulong_t *) pbLastUL);
	  if(vmIsRegKRHB(pbPrev))
	    {
	      vmAssert(!(ulLastUL & krhb_ATTR));
	      pbBack = pbLastUL;
	      pbPrevFree = (char *)(*((ulong_t *) pbBack));
	      if(cbPrev == krhb_REG_MIN_SIZE)
		{
		  vmAssert(!(ulLastUL & krhb_PBBACK));
		}
	      else
		{
		  vmAssert(ulLastUL & krhb_PBBACK);
		  vmAssert(pbPrevFree == pbPrev + krhb_PBBACK);
		}
	    }
	  else	/* Preceding block is attributed */
	    {
	      vmAssert(ulLastUL & krhb_ATTR);
	      pbBack = pbLastUL - sizeof(VMKHBA);
	      pbPrevFree = (char *)(*((ulong_t *) pbBack));
	      if(cbPrev == krhb_ATTR_MIN_SIZE)
		{
		  vmAssert(!(ulLastUL & krhb_PBBACK));
		}
	      else
		{
		  vmAssert(ulLastUL & krhb_PBBACK);
		  vmAssert(pbPrevFree == pbPrev + (krhb_ATTR | krhb_PBBACK));
		}
	    }
	}
    }

  /*
   *  Check that the last block is a valid terminator.	It should map
   *  space up to the end of the committed region.
   */

  vmAssert(vmNextKRHB(pkrh->krh_pbLast) == pkrh->krh_pbEndRes);
  vmAssert(vmOwnerKRHB(pb) == VMKRHLOWNER);

  /*
   *  Walk the free list forwards.  Check each block found is free.
   *  Verify that back pointer is OK.
   */

  pkrhs =  vmFirstPKRHS(pkrh, krhb_REG_MIN_SIZE);
  for(pb = vmHeadFreeKRHB(pkrhs); !vmIsDummyKRHB(pb); pb = vmNextFreeKRHB(pb))
    {
      vmAssert(vmIsFreeKRHB(pb) || vmIsDummyKRHB(pb));
      vmAssert(vmNextFreeKRHB(vmPrevFreeKRHB(pb)) == pb);
    }

  /*
   *  Verify the pointers into the free list.
   */

  cbMax = 0;
  for(pkrhs = vmFirstPKRHS(pkrh, krhb_REG_MIN_SIZE);
	     pkrhs < pkrh->krh_pkrhsLast;
			     pkrhs++)
    {
      vmAssert(vmIsFreeKRHB(vmHeadFreeKRHB(pkrhs)));
      vmAssert(pkrhs->krhs_cbMax > cbMax);
      cbMax = pkrhs->krhs_cbMax;
    }

  return(TRUE);
}

#endif


/***EP	VMFlagsKH - Get heap flags
 *
 *	This function returns a heap flags given its header handle.
 *	An internal error occurs if the header handle is not valid.
 *
 *	ulong_t VMFlagsKH(VMHKH hkh)
 *
 *	ENTRY		hkh		- Heap header handle
 *	RETURN		ulong_t 	- flags (see VMAllocMem)
 *
 *	CALLS		NONE
 *
 *	WARNINGS	NONE
 *
 *	EFFECTS 	NONE
 */

ulong_t VMFlagsKH(hkh)
VMHKH			hkh;		/* Heap header handle */
{
  vmAssert(vmIsHKH(hkh));
  return(apkh[hkh].kh_fl);
}




/***LP	vmSetOwnerKRHB - Set the Owner in a Kernel Resident Heap Block
 *
 *	This procedure sets the given block as being owned by the given
 *	owner id.  For regular blocks, the owner is stored in the block header
 *	(most significant 16-bits).  For large blocks, the owner is stored in
 *	the header of the preceding block.
 *
 *	void vmSetOwnerKRHB(VMKRH *pkrh, char *pb, VMHOB hobOwner)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to block header
 *	RETURN		void
 *
 *	CALLS		vmIsLargeKRHB, vmWriteULKRHB, vmHdrKRHB
 *
 *	EFFECTS 	Writes to heap block header
 *
 *	WARNING 	This function assumes that the hobOwner field
 *			is in bits 16-31 of a regular block's header
 *			or in bits 16-31 of the first ulong of an
 *			attributed block's attribute area.
 */

void vmSetOwnerKRHB(pkrh, pb, hobOwner)
VMKRH			*pkrh;		/* Pointer to heap header */
char			*pb;		/* Pointer to block header */
VMHOB			hobOwner;	/* New owner to be stored */
{
  ulong_t		ulTemp;
  char			*pbTemp = pb;
  if(vmIsAttrKRHB(pb))
    {
      pbTemp = (char *) vmpkhbaKRHB(pb);
    }
  ulTemp = ((vmHdrKRHB(pbTemp) & ~krhb_OWNER_MASK) |
	    (((ulong_t) hobOwner)<<krhb_OWNER_SHIFT));
  vmWriteULKRHB(pkrh, pbTemp, ulTemp);
}


/***LP	vmRoundReqKRHB - Round request size up to actual block size
 *
 *	This function rounds a requested block size up to the actual
 *	size required for the block.  The size of a block header is
 *	added, plus the size of the attribute area (if the block will be
 *	an attributed block), and the result is rounded up to the granularity
 *	of heap block addresses and sizes (currently dword).
 *
 *	ulong_t vmRoundReqKRHB(ulong_t cb, ulong_t fl)
 *
 *	ENTRY		cb		- Requested block size
 *			fl		- Flags
 *					    VMAC_SELALLOC - selector
 *					      mapping of heap block
 *	RETURN		ulong_t 	- Rounded block size
 */

ulong_t vmRoundReqKRHB(cb, fl)
ulong_t 		cb;		/* Requested block size */
ulong_t 		fl;		/* Flags */
{
    register ulong_t  cbRound;

    cbRound = (cb + sizeof(VMKRHB) + krhb_GRAN_MASK) & ~krhb_GRAN_MASK;


    /* 108343 - Round block size to minimum regular block size *BEFORE* */
    /*		adding size for attribute block (if applicable).  This	*/
    /*		is valid as long as:					*/
    /*		krhb_REG_MIN_SIZE = krhb_ATTR_MIN_SIZE - sizeof(VMKHBA) */

    /*
     *	The block must be at least the minimum size required for a regular
     *	block.
     */

    if(cbRound < krhb_REG_MIN_SIZE)
    {
	cbRound = krhb_REG_MIN_SIZE;
    }

    /*
     *	If the block needs to be attributed, add in the size of the attribute
     *	area.
     */

    if(vmNeedsAttrKRHB(cbRound, fl))
    {
	cbRound += sizeof(VMKHBA);
    }

    return(cbRound);
}



/***LP	vmSetAttrKRHB - Set attributes in Kernel Resident Heap Block
 *
 *	Given attribute values, this function sets them in the specified
 *	attributed kernel resident heap block.	The attribute area is located
 *	at the end of the block.
 *
 *	void vmSetAttrKRHB(VMKRH *pkrh, char *pb, VMKHBA *pkhba)
 *
 *	ENTRY		pkrh		- Pointer to heap header
 *			pb		- Pointer to block
 *			pkhba		- Pointer to attributes
 */

void vmSetAttrKRHB(pkrh, pb, pkhba)
VMKRH			*pkrh;		/* Pointer to heap header */
char			*pb;		/* Pointer to block's header */
VMKHBA			*pkhba; 	/* Pointer to attribute data */
{
  /*
   *  BUGBUG - IDB 03/14/90 - Use this vmCopyKHB call when flags can
   *  be passed in.
   *
   *  vmCopyKHB((char *) vmpkhbaKRHB(pb), (char *) pkhba, sizeof(VMKHBA),
   *	fl);
   */

  if(pkrh->krh_fl & PG_W)
    {
      *(vmpkhbaKRHB(pb)) = *pkhba;
    }
  else
    {
      vmWriteULKRHB(pkrh, vmpkhbaKRHB(pb), *((ulong_t *) pkhba));
    }
}



/***EP	VMGetAttrKRHB - Get attributes of Kernel Resident Heap Block
 *
 *	This function reads the attributes of the specified attributed
 *	Kernel Resident Heap Block.
 *
 *	void VMGetAttrKRHB(char *pbData, VMKHBA *pkhba)
 *
 *	ENTRY		pbData		- Pointer to block
 *			pkhba		- Pointer to attributes
 */

void VMGetAttrKRHB(pbData, pkhba)
char			*pbData;	/* Pointer to block's header */
VMKHBA			*pkhba; 	/* Pointer to attribute data */
{
  char			*pb = pbData - sizeof(VMKRHB);
					/* Pointer to block's header */

  if(vmIsAttrKRHB(pb))
    {
      *pkhba = *((VMKHBA *) vmpkhbaKRHB(pb));
    }
  else
    {
      pkhba->khba_hobOwner = vmOwnerRegKRHB(pb);
      pkhba->khba_sel = (SEL) NA;
    }
}


/***EP	VMSelKHB - Get Selector Mapping Kernel Heap Block
 *
 *	This function returns the selector (if any) that maps the given
 *	Kernel Heap Block.  The given parameters are assumed to be valid.
 *
 *	SEL VMSelKHB(VMHKH hkh, char *pbData)
 *
 *	ENTRY		hkh		- Heap handle
 *			pbData		- Pointer to block's data
 *	RETURN		SEL		- Selector (if any), or NULL
 */

SEL VMSelKHB(hkh, pbData)
VMHKH			hkh;		/* Heap handle */
char			*pbData;	/* Pointer to block's data */
{
  if(vmIsHKRH(hkh))
    {
      return(vmIsAttrKRHB(pbData - sizeof(VMKRHB)) ?
	     vmpkhbaKRHB(pbData - sizeof(VMKRHB))->khba_sel : (SEL) NA);
    }
  return(((VMKSHB *)(pbData - sizeof(VMKSHB)))->kshb_sel);
}


/***EP	VMOwnerKHB - Get Owner of Kernel Heap Block
 *
 *	This function returns the owner of the given Kernel Heap Block.
 *	The given parameters are assumed to be valid.
 *
 *	VMHOB VMOwnerKHB(VMHKH hkh, char *pbData)
 *
 *	ENTRY		hkh		- Heap handle
 *			pbData		- Pointer to block's data
 *	RETURN		VMHOB		- Heap block's hobOwner
 */

VMHOB VMOwnerKHB(hkh, pbData)
VMHKH			hkh;		/* Heap handle */
char			*pbData;	/* Pointer to block's data */
{
  if(vmIsHKRH(hkh))
    {
      return(vmIsAttrKRHB(pbData - sizeof(VMKRHB)) ?
	     vmpkhbaKRHB(pbData - sizeof(VMKRHB))->khba_hobOwner :
	     ((VMKRHB *)(pbData - sizeof(VMKRHB)))->krhb_hobOwner);
    }
  return(((VMKSHB *)(pbData - sizeof(VMKSHB)))->kshb_hobowner);
}

/***EP	VMGetInfoKHB - return heap handle info corresponding to linear address.
 *
 *	BUGBUG replaced by VMGetKHBI
 *
 *	VMGetInfoKHB returns heap handle and flags given a linear address
 *	within a heap. If the given address is not within any heap
 *	an error is returned.
 *
 *	ENTRY		p		- Linear address within a heap
 *			pkhib		- Structure for return information
 *	RETURN		int		- Result code (NO_ERROR if successful)
 *
 */

int	VMGetInfoKHB(p, pkhib)
register char		*p;		/* Linear address to check */
VMKHIB			*pkhib; 	/* Structure for return info. */
{
  int			rc;
  VMKHBI		khbi;

  khbi.khbi_pbData = p;
  if((rc = VMGetKHBI(VMKHBI_HEAP, SSToDS(&khbi))) == NO_ERROR)
    {
      pkhib->khib_fl = khbi.khbi_fl;
      pkhib->khib_hkh = khbi.khbi_hkh;
    }
  return(rc);
}


/***EP	VMValidateKHBAddress - Validate that the given address is the
 *
 *	BUGBUG - This is replaced by VMGetKHBI
 *
 *	start of a heap block, and return the owner of the block if requested
 *
 *	ENTRY		p		- Address to validate
 *			phobOwner	- Location to return the owner
 *	RETURN		int		- Error code
 */

int			VMValidateKHBAddress(p, phobOwner)
char			*p;
VMHOB			*phobOwner;
{
  int			rc;
  VMKHBI		khbi;
  khbi.khbi_pbData = p;
  if((rc = VMGetKHBI(VMKHBI_BLOCK, SSToDS(&khbi))) == NO_ERROR)
    {
      *phobOwner = khbi.khbi_hobOwner;
    }
  return(rc);
}

#ifdef VMSTRICT

/***EP	VMPrintKRH - print all kernel resident heap blocks
 *
 *	print all kernel resident heap blocks in a kernel resident heap
 *
 *	ENTRY		NONE
 *
 *	RETURN		NONE
 */

void VMPrintKRH(VMHKH hkh)
{
  register VMKRHB  *pkrhb;
  register ulong_t ulSize;
  register VMKHBA  *pkhba;

  if(vmIsHKRH(hkh))
  {
      pkrhb = (VMKRHB *) (apkh[hkh].kh_pkrh)->krh_pbFirst;

      do
      {
	  if(vmIsAttrKRHB(pkrhb))
	  {
	      ulSize = pkrhb->krhb_ulHdr & krhb_ATTR_SIZE_MASK;
	      (uchar_t *) pkhba  = (uchar_t *) pkrhb + ulSize - sizeof(VMKHBA);
	      printf("%lx %lx %x %x %x\n",pkrhb, ulSize, pkhba->khba_hobOwner,
				      pkhba->khba_sel, pkhba->khba_hobMTE);
	  }
	  else
	  {
	      ulSize = (ulong_t) pkrhb->krhb_usSize & krhb_REG_SIZE_MASK;
	      printf("%lx %x %x\n",pkrhb, ulSize, pkrhb->krhb_hobOwner);
	  }

	  (uchar_t *) pkrhb += ulSize;

      }while(ulSize >= krhb_REG_MIN_SIZE);
  }
}
#endif

/***EP	VMRequestSems - grab kernel resident heap sem, then pgCommitSem
 *
 *	called from CMAttendSwapper( ROM.ASM ) defect 77961
 *
 *	ENTRY		NONE
 *
 *	RETURN		int		- Error code
 */

int VMRequestSems( int dummy )
{
  int			rc;		/* Return code */
  VMKRH 		*pkrh;		/* Resident Heap Pointer */

  pkrh = apkh[VM_HKH_PUB_RESRW].kh_pkrh;
  vmAssert(vmIsValidKRH(pkrh));

  if ((rc = KSEMRequestMutex((HKSEM)&(pkrh->krh_ksem), -1))
	 != NO_ERROR)
     {
     return(rc);
     }
  if ((rc = KSEMRequestMutex((HKSEM)&pgCommitSem, -1))
	 != NO_ERROR)
     {
     KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
     return(rc);
     }

  return( NO_ERROR );
}

/***EP	VMReleaseSems - frees pgCommitSem, then kernel resident heap sem
 *
 *	called from CMAttendSwapper( ROM.ASM ) defect 77961
 *
 *	ENTRY		NONE
 *
 *	RETURN		NONE
 */

void VMReleaseSems( int dummy )
{
  VMKRH 		*pkrh;		/* Resident Heap Pointer */

  pkrh = apkh[VM_HKH_PUB_RESRW].kh_pkrh;
  vmAssert(vmIsValidKRH(pkrh));

  KSEMReleaseMutex((HKSEM)&pgCommitSem);
  KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
}





#pragma code_seg("CINIT_TEXT", "CODE")      /* This section goes to INIT code */

/***EP	VMDonateKHB - Donate Memory Permanently to Public Kernel Heap
 *
 *	This function frees complete pages mapped to a range of linear
 *	memory and maps any preceding and succeeding page fragments so that
 *	they can be used by the appropriate public kernel heap.  The physical
 *	pages containing the fragments mapped to the beginning and end of
 *	the linear address region are aliased to heap linear memory.
 *
 *	int VMDonateKHB(char *pbRegion, ulong_t cbRegion, ulong_t fl)
 *
 *	ENTRY		pbRegion	- Pointer to data of block to be donated
 *			cbRegion	- Size of block
 *			fl		- Flags
 *					    PG_R - memory is readable
 *					    PG_W - memory is writeable
 *	RETURN		int		- Result code (NO_ERROR if successful)
 */

int VMDonateKHB(pbRegion, cbRegion, fl)
char			*pbRegion;		/* Address of donor region */
ulong_t 		cbRegion;		/* Length of donor region */
ulong_t 		fl;			/* flags */
{
  char			*pbDecommitMin; 	/* First pg bdry in block */
  char			*pbDecommitMax; 	/* Last pg bdry in block */
  int			rc;			/* result code */

  /*
   *  Validate flags.  Read and/or Write access permissions must be
   *  specified.  No other flags are allowed.
   */

  vmAssert(fl == (fl & (PG_R | PG_W | PG_STATE)));
  vmAssert(fl & (PG_R | PG_W));
  if(fl & PG_W)
    {
      fl |= PG_R;
    }

  /*
   *  Check that block size is > 0 and that region is in the system arena.
   */

  vmAssert(cbRegion > 0);
  vmAssert(VMSysArena((ulong_t) pbRegion));

  /*
   *  Calculate range of interior pages to be freed and free them.
   */

  pbDecommitMin = (char *) vmPageCeil(pbRegion);
  pbDecommitMax = (char *) vmPageFloor(pbRegion + cbRegion);
  if(pbDecommitMin < pbDecommitMax)
    {
      rc = PGFree(((ulong_t) pbDecommitMin) >> PAGESHIFT,
		  pPTDACur,
		  ((ulong_t)(pbDecommitMax - pbDecommitMin)) >> PAGESHIFT,
		  PG_CHKLOCK);
      vmAssert(rc == NO_ERROR);
    }

  /*
   *  Donate the fragments (if any) preceding and following the freed
   *  pages.
   */

// perf - mem reduction : for retail and hstrict builds combine
//			  RES1MRO and RES1MRW and RESRO with RESRW
//			  so change r/o pages to r/w
#ifndef VMSTRICT
   if ((fl & PG_W) == 0)
     {
       fl |= PG_W;
       rc = PGSetAccess((ulong_t)pbRegion >> PAGESHIFT,
		       NA,
		       1,
		       PG_RESIDENT | PG_W | PG_R);
     }
#endif

  if(pbDecommitMin > pbRegion + cbRegion && pbDecommitMax < pbRegion)
    {
      rc = vmDonateFragKH(pbRegion, cbRegion, fl);
      return(rc);
    }


  if(pbDecommitMax < pbRegion + cbRegion)
    {
      rc = vmDonateFragKH(pbDecommitMax,
			  pbRegion + cbRegion - pbDecommitMax,
			  fl);
    }
  if(pbDecommitMin > pbRegion)
    {
      rc = vmDonateFragKH(pbRegion, pbDecommitMin - pbRegion, fl);
    }

  return(rc);
}


/***LP	vmDonateFragKH - Donate Memory Permanently to Kernel Heap
 *
 *	This function donates a resident page fragment permanently to the
 *	public kernel r/w or read-only resident heap.  Only the maximal
 *	sub-block with heap-granular address and size will be donated.	Only
 *	fragments that are aligned to the beginning or the end of a page
 *	will be donated, because an additional page of heap would have to
 *	be committed in order to donate an interior page fragment.  A zero
 *	size may be specified, for convenience.
 *
 *	NOTE:  The page fragment will be mapped to a linear page within
 *	a free block as follows.
 *
 *	[1] Page fragment aligned to beginning of page:
 *
 *	The highest decommitted page within the free block is mapped
 *	to the donor page.  The original free block is split into two
 *	free blocks and a busy block is created to bridge the region
 *	of the linear page that corresponds to memory not being donated.
 *
 *	The following diagrams illustrate the effect of donation on
 *	the selected free block.  Symbols are used as follows:
 *
 *	P and | mark linear address page boundaries
 *	blank regions represent decommitted memory
 *	/,L,A,H denote committed memory; L is a back pointer to a free
 *	block's header, A is the attribute area and H is the header.
 *	B = linear address space occupied by a busy block, F by a free block.
 *
 *	Free block before donation of page aligned fragment at end of region:
 *
 *	-P---------P----------...	...---P---------P---------P--
 *	 |///AH////|			      | 	|///LAH///|
 *	----------------------...	...--------------------------
 *	 |BBBBFFFFF|FFFFFFFFFFFFFFFFFFFFFFFFFF|FFFFFFFFF|FFFFFBBBB|
 *
 *	Free block after donation of page aligned fragment at end of region:
 *
 *	-P---------P----------...	...---P---------P---------P--
 *	 |///AH////|			      |//LAHUUUU|H//LAH///|
 *	----------------------...	...--------------------------
 *	 |BBBBFFFFF|FFFFFFFFFFFFFFFFFFFFFFFFFF|FFFFBBBBB|FFFFFBBBB|
 *
 *	[2] Page fragment aligned to end of page
 *
 *	The lowest decommitted page within the free block is mapped
 *	to the donor page.  The original free block is split into two
 *	free blocks and a busy block is created to bridge the region
 *	of the linear page that corresponds to memory not being donated.
 *	int vmDonateFragKH(char *pbRegion, ulong_t cbRegion, ulong_t fl)
 *
 *	Free block before donation of page aligned fragment at start of region:
 *
 *	-P---------P---------P--...		   ...--P---------P--
 *	 |///AH////|	     |				|///BAH///|
 *	------------------------...		   ...---------------
 *
 *	Free block after donation of page aligned fragment at start of region:
 *
 *	-P---------P---------P--...		   ...--P---------P--
 *	 |///AH//BH|UUU  H///|				|///BAH///|
 *
 *	Free block after donation of page aligned fragment at end of region:
 *
 *	ENTRY		pbRegion	- Pointer to region to be donated
 *			cbRegion	- Size of region.
 *			fl		- Flags
 *					    PG_R - memory is readable
 *					    PG_W - memory is writeable
 *	RETURN		int		- result code
 */

int vmDonateFragKH(pbRegion, cbRegion, fl)
char			*pbRegion;	/* Pointer to donor block */
ulong_t 		cbRegion;	/* Size of donor block */
ulong_t 		fl;		/* Flags */
{
  char			*pbRegionEnd;	/* End addr of usable sub-block */
  char			*pbBusy;	/* Address of perm. busy block */
  char			*pbFreeBefore;	/* Address of free block bef. busy */
  char			*pbFreeAfter;	/* Address of free block after busy */
  char			*pbCommit;	/* Start of region to commit */
  char			*pbRegionPgFloor; /* Page floor for region */
  char			*pbRegionPgCeil; /* Page ceiling for region */
  VMKRH 		*pkrh;		/* Heap header pointer */
  VMKRHS		*pkrhs; 	/* Freelist section pointer */
  ulong_t		cbFree; 	/* Count of free bytes */
  ulong_t		rc;		/* result code */
  VMHKH 		hkh;		/* Heap header handle */
  vmAssert(!vmCrossesPgBdry(pbRegion, cbRegion));

  /*
   *  Determine which heap to donate the fragment to.
   */

// perf - mem reduction : for retail and hstrict builds combine
//			  RES1MRO and RES1MRW and RESRO with RESRW
#ifdef VMSTRICT
  hkh = (fl & PG_W) ? VM_HKH_PUB_RESRW : VM_HKH_PUB_RESRO;
#else
  hkh = VM_HKH_PUB_RESRW;
#endif
  pkrh = apkh[hkh].kh_pkrh;

  /*
   *  Calculate the largest sub-block having heap-granular address and size.
   *  Select appropriate public resident heap according to access permissions.
   */

  pbRegionEnd = vmFloorKRHB(pbRegion + cbRegion);
  pbRegion = vmCeilKRHB(pbRegion);
  cbRegion = pbRegionEnd - pbRegion;
  if(cbRegion < krhb_REG_MIN_SIZE)
    {
      return(NO_ERROR);
    }
  pbRegionPgFloor = (char *) vmPageFloor(pbRegion);
  pbRegionPgCeil = (char *) vmPageCeil(pbRegion + cbRegion);

  /*
   *  Perform the donation.
   */
  rc = NO_ERROR;
  do
    {
      if (rc != NO_ERROR)  /* from while at end of loop */
	{
	  while ((rc = KSEMRequestMutex((HKSEM)&(pkrh->krh_ksem), -1))
		 != NO_ERROR);	 /* ignore interrupts */
	  KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
	}
      /*
       *  Find a large enough free block.  Any block with at least one
       *  decommitted page will do; if a block has a size at least two
       *  pages plus the size of the free block header plus the size of
       *  the attribute area structure, it must have at least one decommitted
       *  page.  First get a pointer to the lowest free list section that
       *  could contain such a block and then scan forwards looking for the
       *  first block that is large enough.
       */

      pkrhs = vmFirstPKRHS(pkrh, 2*PAGESIZE+sizeof(VMKRHF)+sizeof(VMKHBA));
      for(pbFreeBefore = vmHeadFreeKRHB(pkrhs);
	  vmSizeKRHB(pbFreeBefore) < 2*PAGESIZE+sizeof(VMKRHF)+sizeof(VMKHBA);
	  pbFreeBefore = vmNextFreeKRHB(pbFreeBefore))
	{
	  if(vmIsDummyKRHB(pbFreeBefore))
	    {
	      return(ERROR_NOT_ENOUGH_MEMORY);
	    }
	}

      /*
       *  Determine the address of the page to be committed, and calculate
       *  addresses for the busy block and following free block.
       */

      cbFree = vmSizeKRHB(pbFreeBefore);
      if(pbRegion == (char *) vmPageFloor(pbRegion))
	{
	  /*
	   *  We're donating the page-aligned fragment at the end of a region.
	   *  The last decommitted page of the selected free block will be
	   *  mapped.  The non-donated portion of the donor page resides at the
	   *  end of the page, so a permanently busy block is needed to map out
	   *  the linear address space corresponding to that portion.  The busy
	   *  block's header starts <size of region - header size> into that
	   *  linear page.  The following free block's header starts at the
	   *  next page, i.e. at the end of the decommitted region of the
	   *  original free block.
	   */

	  pbFreeAfter = vmDecommitMaxKRHB(pbFreeBefore, cbFree);
	  pbCommit = pbFreeAfter - PAGESIZE;
	  pbBusy = pbFreeAfter - PAGESIZE + cbRegion - sizeof(VMKRHB);
	}
      else if(pbRegion + cbRegion == (char *) vmPageCeil(pbRegion + cbRegion))
	{
	  /*
	   *  We're donating the page fragment at the beginning of a region.
	   *  This fragment is aligned to the end of a page.  The first
	   *  decommitted page of the selected free block will be mapped.  The
	   *  non-donated portion of the donor page resides at the beginning
	   *  of the page, so a permanently busy block is needed to map out
	   *  the linear address space corresponding to that portion.  The
	   *  busy block's header starts <header size bytes before the first
	   *  decommitted page.  The following free block's header starts
	   *  <PAGESIZE - size of region - header size> bytes into that page.
	   */

	  pbCommit = vmDecommitMinKRHB(pbFreeBefore);
	  pbBusy = pbCommit - sizeof(VMKRHB);
	  pbFreeAfter = pbCommit + PAGESIZE - cbRegion;
	}
      else
	{
	  /*
	   *  We're donating an interior region.  This is not supported
	   *  currently, since an additional page of heap would have to
	   *  be committed and since interior region donation is rare.
	   *  Return NO_ERROR to allow calls to be made to the donate routine.
	   */

	  return(NO_ERROR);
	}

      /*
       *  Take the heap semaphore.  Ignore signals since we're freeing a
       *  resource.  Restart the operation if we had to wait.
       */
    }
  while ( (rc=KSEMRequestMutex((HKSEM)&(pkrh->krh_ksem), 0)) != NO_ERROR);
  vmAssert(rc == 0);
  /*
   *  Validate heap.
   */

  vmAssert(vmIsValidKRH(pkrh));

  vmRemoveFreeKRHB(pkrh, pbFreeBefore);

  /*
   *  Attach the first or last decommitted page of the free heap block.
   */

  if((rc = PGAttach(((ulong_t) pbCommit) >> PAGESHIFT,/* Next heap page */
		    pPTDACur,			      /* Tgt PTDA = Current */
		    (((ulong_t) pbRegionPgFloor) >> PAGESHIFT),
		    pPTDACur,			      /* Srce PTDA = current */
		    1,				      /* Attach 1 page */
		    fl | PG_RESIDENT)) != NO_ERROR)
    {
      KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
      return(rc);
    }

  /*
   *  Update the heap by carving the free block into free,busy,free.  This
   *  section must not block even though we have the heap semaphore
   *  and have removed the block from the free list.
   */

  TKBeginNoBlock();

  /*
   *  Write free block following the busy block.
   */

  vmCreateKRHB(pkrh,
	       pbFreeAfter,
	       pbFreeBefore + cbFree  - pbFreeAfter,
	       pkhbaFree,
	       krhb_CR_NXVAL | krhb_CR_NEW | krhb_CR_NOINSFR);

  /*
   *  Write the busy block.
   */

  vmCreateKRHB(pkrh, pbBusy, pbFreeAfter - pbBusy, pkhbaDBusy,
		krhb_CR_NXVAL | krhb_CR_NEW);

  /*
   *  Write the free block preceding the busy block.
   */

  vmCreateKRHB(pkrh, pbFreeBefore, pbBusy - pbFreeBefore, pkhbaFree,
		krhb_CR_NXVAL | krhb_CR_NEW);
  vmInsertFreeKRHB(pkrh, pbFreeAfter);

  TKEndNoBlock();

  /*
   *  Release the heap semaphore.
   */

  KSEMReleaseMutex((HKSEM)&(pkrh->krh_ksem));
  vmAssert(vmIsValidKRH(pkrh));
  return(rc);
}


/***EP	VMCreateKH - Create a Kernel Heap
 *
 *	This function creates a new kernel heap.  Certain properties
 *	of the heap are specifiable.
 *
 *	int VMCreateKH(ulong_t cb, ulong_t fl, VMHOB hobOwner, VMHKH *phkh,
 *		       VMAC *pac)
 *
 *	ENTRY		cb		- Size of heap in bytes (includes
 *					  heap header for read/write heaps)
 *					  This will be rounded up to page
 *					  granularity.
 *			fl		- Flags global to all heap blocks
 *					  PG_RESIDENT - all heap objects
 *							 are to be resident
 *					  PG_SWAPPABLE - all heap objects
 *							 are to be swappable
 *					  PG_R - all heap objects are readable
 *					  PG_W - all heap objects are writable
 *					  PG_X - all heap objects are exec.
 *			hobOwner	- Resident Heap Owner
 *			phkh		- Pointer to location in which the
 *					  heap header handle will be returned.
 *					  This handle is specified on allocat-
 *					  ions and frees from the heap.
 *			pac		- Pointer to VMAllocMem info
 *	RETURN		int		- Result code
 */

int VMCreateKH(cb, fl, hobOwner, phkh, pac)
ulong_t 		cb;		/* Heap virtual size in bytes */
ulong_t 		fl;		/* Flags global to all heap blocks */
VMHOB			hobOwner;	/* Heap's owner */
VMHKH			*phkh;		/* Pointer to where handle will be
					   returned */
VMAC			*pac;		/* Info from VMInit or VMAllocMem */
{
  ulong_t		cbReserve;	/* Virtual Size of heap */
  ulong_t		i;		/* Freelist section index */
  ulong_t		lpgBaseCommit;	/* Base lin page to commit */
  ulong_t		lpgBase;	/* Base lin page of heap */
  VMKRHY		*pkrhy; 	/* Pointer to yield structure */
  VMKRH 		*pkrh;		/* Heap header */
  VMKRHS		*pkrhs; 	/* Free list header pointer */
  char			*pbDummy;	/* Dummy free block */
  VMAC			ac;		/* VMAllocMem info */
  int			rc;		/* Return code routines */
  ushort_t		c;		/* Counter */
  VMHKH 		hkh;		/* New heap handle */

  /*
   *  If info for VMAllocMem was given, copy it to the stack.
   */

  if(pac != NULL)
    {
      ac.ac_va = pac->ac_va;
      ac.ac_hob = pac->ac_hob;
    }

  /*
   *  Validate flags.  One of PG_RESIDENT or PG_SWAPPABLE must be specified.
   *  If creating swappable heap call swappable heap creation function.
   */

  vmAssert(fl == (fl & (PG_STATE | PG_R | PG_W | PG_X | PG_1M)));
  if((fl & PG_RESIDENT) == 0)
    {
      return(vmCreateKSH(cb, fl, hobOwner, phkh));
    }

  /*
   *  The size of a resident heap header must be a power of 2.	This is
   *  because the last n bits of a heap block address are not available.
   */

  vmAssert((sizeof(VMKRHB) & (sizeof(VMKRHB) - 1)) == 0);

  /*
   *  Check that the limit on the number of resident heaps in the system
   *  will not be exceeded.  Find available slot and reserve it.  Update
   *  maximum handle given out so far.
   */

  for(hkh = hkh_MIN; hkh <= hkh_MAX; hkh++)
    {
      if(apkh[hkh].kh_fl == NA)
	{
	  if(hkh > hkhCurrMax)
	    {
	      hkhCurrMax = hkh;
	    }
	  break;
	}
    }
  vmAssert(hkh <= hkh_MAX);
  apkh[hkh].kh_fl = fl;

  /*
   *  Except for the special case of the first heap, create an object for the
   *  new kernel resident heap and commit one page to beginning and end.
   */

  cbReserve = vmPageCeil(cb);
  if(vmInitFlags & VMS_COMMIT)
    {
      if((rc = VMAllocMem(cbReserve,	/* Size to reserve */
		      PAGESIZE, 	/* Commit 1 page */
		      fl | krh_RESERVE, /* alloc flags */
		      HOBNULL,		/* No PTDA */
		      hobOwner, 	/* Owned by specified owner */
		      HOBNULL,		/* Not an MTE object */
		      NA,		/* No Selector Manager flags */
		      NA,		/* No block number */
		      SSToDS(&ac)))
	!= NO_ERROR)
	{
	  goto err1;
	}

      /*
       *  Commit the last page of the heap.
       */

      lpgBaseCommit = (ac.ac_va + cbReserve - PAGESIZE) >> PAGESHIFT;
      lpgBase = ac.ac_va >> PAGESHIFT;
      if((rc = PGAlloc((ac.ac_va + cbReserve - PAGESIZE) >> PAGESHIFT,
		       NULL,
		       1,
		       NA,
		       ac.ac_hob,
		       lpgBaseCommit - lpgBase,
		       fl | krh_COMMIT)) != NO_ERROR)
	{
	  goto err2;
	}
    }

  /*
   *  Create the resident heap header.	For read/write heaps, the
   *  header is written to the first page of the heap.	For read-only
   *  heaps, a header block is allocated from the Public Resident
   *  Read/Write heap.	Note that this works for the case where we are
   *  creating the Public Read/Write Resident heap itself, provided
   *  that this is the very first heap to be created.
   */

  if(fl & PG_W)
    {
      /*
       *  Heap is read/write.  Since write access is allowed, we scratch
       *  the header to the start of the first page committed for the
       *  heap.  The first page therefore consists of the heap header,
       *  first free block and end marker.  Note that the start address
       *  for the first block must be rounded up to the granularity required
       *  for resident heap block addresses.
       */

      pkrh = (VMKRH *) ac.ac_va;
      pkrh->krh_pbFirst = (char *)(vmCeilKRHB(ac.ac_va + sizeof(VMKRH)));
    }
  else
    {
      /*
       *  Heap is read only.  Since write access is not allowed, we
       *  allocate the heap header from the writable heap and so all of
       *  heap can be initialized as a free block followed by an end
       *  marker block.
       */

      if((rc = allocKRHB(sizeof(VMKRH),
		   hobOwner,
		   (char **) SSToDS(&pkrh))) != NO_ERROR)
	{
	  goto err2;
	}
      pkrh->krh_pbFirst = (char *) ac.ac_va;
    }

  KSEMInit((HKSEM)&(pkrh->krh_ksem), KRNL_MUTEXSEM, 0);
  /*
   *  Initialize heap header and last free list pointer.
   */
  pkrh->krh_hob = ac.ac_hob;
  pkrh->krh_lpgBase = lpgBase = (ac.ac_va >> PAGESHIFT);
  pkrh->krh_pbEndRes = (char *)(ac.ac_va + cbReserve);
  pkrh->krh_pkrhsLast = &(pkrh->krh_akrhs[krh_CFREELIST - 1]);
  pkrh->krh_fl = fl | krh_COMMIT;
  pkrh->krh_pbLast = pkrh->krh_pbEndRes - sizeof(VMKRHB);

  /*
   *  Create end of heap marker block.	Note that the end of
   *  heap marker exists as a convenience so that the code that
   *  updates a succeeding block's header "previous block is free
   *  flag" does not have to special case on the allocation or
   *  freeing of the last normal block.
   */

  vmCreateKRHB(pkrh, pkrh->krh_pbLast, sizeof(VMKRHB), pkhbaLast, krhb_CR_NEW);

  /*
   *  Initialize the free list to consist of the dummy block
   *  (embedded in the heap header).  Initialize dummy block with
   *  low size value so that it is never actually given out.
   */

  pbDummy = (char *) &(pkrh->krh_krhfDummy);
  vmNextFreeKRHB(pbDummy) = pbDummy;
  vmPrevFreeKRHB(pbDummy) = pbDummy;
  pkrh->krh_krhfDummy.krhf_ulHdr =
    vmMakeHdrRegKRHB(VMKRHFOWNER, krhb_REG_MIN_SIZE, NA);

  /*
   *  Initialize each free list section header to point to the
   *  start of the free list (dummy block)and set the maximum
   *  block sizes for each free list section.  The max block size
   *  size for the final free list section is set equal to the
   *  maximum heap size specified on entry, rounded up to a page
   *  multiple, minus the size of the heap header if writable.
   */

  for(i = 0,pkrhs = &(pkrh->krh_akrhs[0]);
    pkrhs <= &(pkrh->krh_akrhs[krh_CFREELIST - 1]); i++,pkrhs++)
    {
      vmHeadFreeKRHB(pkrhs) = pbDummy;
      pkrhs->krhs_cbMax = ackrhsInit[i];
    }
  vmHeadFreeKRHB((&(pkrh->krh_akrhs[0]))) = (char *) NULL;

  /*
   *  Create the initial free block mapping all of the heap.
   *  Add block to free list.
   */

  vmCreateKRHB(pkrh,
	       pkrh->krh_pbFirst,
	       (ulong_t)(pkrh->krh_pbLast)-
	       (ulong_t)(pkrh->krh_pbFirst),
	       pkhbaFree,
	       krhb_CR_NEW | krhb_CR_NXVAL);

  /*
   *  Initialize the yield array for this heap.
   */

  pkrhy = pkrh->krh_akrhy;
  for(c = krhb_MAXYIELD; c-- > 0;)
    {
      pkrhy->krhy_pb = NULL;
      (pkrhy++)->krhy_cyield = 0;		/* Zero yield count */
    }

 /*
  *  Update minimum and maximum heap region (all heaps).
  */

 if(vmladdrMinKH > (ulong_t) pkrh->krh_pbFirst)
   {
     vmladdrMinKH = (ulong_t) pkrh->krh_pbFirst;
   }
 if(vmladdrMaxKH < (ulong_t) pkrh->krh_pbEndRes)
   {
     vmladdrMaxKH = (ulong_t) pkrh->krh_pbEndRes;
   }

  /*
   *  Initialize the newly created resident heap handle and return
   *  it.
   */

  apkh[hkh].kh_pkrh = pkrh;
  apkh[hkh].kh_fl = fl;
  *phkh = hkh;

  return(NO_ERROR);

  /*
   *  Last page decommit or read-only heap header allocation failed.  Free
   *  the heap object and drop thru..
   */

err2:
  while(VMFreeMem(ac.ac_va, HOBNULL, NA) == ERROR_INTERRUPT);

err1:

  /*
   *  Error occurred creating heap.  Return error.
   */

  return(rc);
}

#pragma code_seg()
