#define SCCSID	"@(#)vmfree.c   6.12 92/02/14"

/*	 IBM/Microsoft Confidential
 *
 *	 Copyright (c) IBM Corporation 1987, 1989
 *	 Copyright (c) Microsoft Corporation 1987, 1989
 *
 *	 All Rights Reserved
 *
 *	 History of Changes:
 *  09/18/91  Khoa Dang Huynh
 *	 CP20 DCR 1486	Provide segment packing capability.
 *  10/12/95  Scott Cottrill
 *	 127663  -  Ensure release of vmNamShrSem in vmFreeMemNotifyOd
 *  12/22/95   ChgTeam
 *	 D147433  - Commented out vmFreeNamedSharedMem from fix 127663
 *  05/11/96 147968  RJM Change FSD and DD system owners to use HMTEs
 *  05/24/96  Khoa Dang Huynh
 *	 D155214  - Make sure that the free hint pointer in the shared
 *		    arena is not inadvertently set to the boundary
 *		    sentinel which points to the top of the R/O GSR.
 *  05/28/96  Khoa Dang Huynh
 *	 D156118  - Correct the checking of AR_HCO flag in several
 *		    places in this file.
 */


#define INCL_DOSERRORS
#include <bseerr.h>
#include <os2defp.h>

#define INCL_TYPES
#include  <basedef.h>
#include  <bsedosp.h>

#include		<dossymc.h>
#include		<sigdef.h>
#include		<except.h>

#define FNO_NUM FNO_VMFREE_C
#include		<fnomem.h>
#include		<osmac.h>
#include		<mi.h>
#include		<pgexport.h>
#include		<vmexport.h>
#include		<vmlocal.h>
#include		<vmowner.h>
#include		<selxport.h>
#include		<seldesc.h>
#include		<tkexport.h>
#include		<infoseg.h>
#include		<curdir.h>
#include		<dirent.h>
#include		<find.h>
#include		<screen.h>
#include		<sem.h>
#include		<tkptda.h>
#include		<ldrxport.h>

#ifdef INCDEBUG
#include		<dbgex.h>
#endif /* DEBUG */

#include		<mmph.h>
#include		<semxport.h>

#define FOR_EXEHDR	1 /*147968 */
#include <exe386.h>	  /*147968 */
#include <ldrvars.h>	  /*147968 */

#ifndef NULL
#define NULL  0
#endif

extern ulong_t	vmGlobalShrLBDRY; // GSR - BUGBUG should be in vmlocal.h


/***EP	VMVirtFree - free an ordinary arena entry
 *
 *	This procedure removes the specified arena record from
 *	its arena.
 *
 *	VMVirtFree (par, pah)
 *
 *	ENTRY	par		- arena record pointer
 *		pah		- arena header pointer
 *	RETURN	NONE
 *
 *	Delete the specified record from the list.  If the previous
 *	record is a boundary sentinel, then adjust its size upwards
 *	as needed to provide room for growth from below.
 *
 *	CALLS	vmParToHar, vmArMap, vmArFree, vmFreeGrpArena,
 *		vmEraseOd
 *
 *	WARNING This procedure may block when the arena record is
 *		returned to the free pool
 *
 *	WARNING This procedure must remain in resident code.
 *
 *	EFFECTS Frees an arena record.	May change size in boundary
 *		sentinel.  May free an object group arena.  May
 *		remove information from Locate Tables.
 */

void			VMVirtFree(par,pah)
register VMAR		*par;		/* Arena record pointer */
VMAH			*pah;		/* Arena header pointer */
{
    ulong_t		ipgmaxprv;	/* Highest private virtual page */
    register VMAR	*parNext;	/* Next arena record */
    VMAR		*parParent;	/* Parent arena record */
    register VMAR	*parPrev;	/* Previous arena record */

    vmAssert(!vmSentAr(par));		/* Must not free sentinel */
    vmAssert(par->ar_harlink == HARNULL);
					/* No lateral link */
    parPrev = vmArMap(par->ar_harprev); /* Point to previous record */
    parNext = vmArMap(par->ar_harnext); /* Point previous to next */
    parPrev->ar_harnext = par->ar_harnext;
					/* Point previous to next */
    parNext->ar_harprev = par->ar_harprev;
					/* Point next to previous */
    if(pah != NULL)			/* If there is a bitmap entry */
    {
	vmAssert(vmRegAr(par)); 	/* Must be regular entry */
	vmEraseOd(pah, par);		/* Erase object's location data  */

	/*
	 *  Adjust the free region hint pointer.
	 */
	if(pah->ah_parSen->ar_harlink != HARNULL &&
	       vmBdrySentAr(vmArMap(pah->ah_parSen->ar_harlink)))
	{
	    /*
	     *	This is an expand up arena, therefore adjust the hint pointer
	     *	down if possible.
	     */
	    if(pah->ah_parFree == par ||
	    pah->ah_parFree->ar_ipg > parPrev->ar_ipg)
	    {
		pah->ah_parFree = parPrev;
	    }
	}
	else
	{
	    /*
	     *	This is an expand down arena, therefore adjust the hint pointer
	     *	up if possible.  Keep the hint pointer out of the protected
	     *	region of the shared arena.
	     *
	     * KDH - Make sure that the hint pointer is out of the GSR.
	     *	     (Defect 155214)
	     */
	    if(!(vmSentAr(pah->ah_parFree)) &&
	       pah->ah_parFree->ar_ipg <= par->ar_ipg &&
	       (!VMShrArenaPg(pah->ah_parFree->ar_ipg) ||
// GSR start
		((parNext->ar_ipg <= (vmGlobalShrLBDRY >> PAGESHIFT)) &&
		 (!vmSentAr(parNext)))))
// GSR end
	    {
		pah->ah_parFree = parNext;
	    }
	}
    }

    /*
     *	If the next record is an ordinary sentinel, then
     *	we just deleted the highest record from the arena.
     *	If the sentinel is linked to a boundary sentinel,
     *	then adjust the sentinel's ar_ipgmax field downwards
     *	as necessary.
     */

    if(vmOrdSentAr(parNext))
    {					/* If ordinary sentinel next */
	if(parNext->ar_harlink != HARNULL)
					/* If there is a parent record */
	{
	    parParent = vmArMap(parNext->ar_harlink);
					/* Point to parent record */
	    if(vmBdrySentAr(parParent))
	    {				/* If parent is boundary */
		ipgmaxprv = parPrev->ar_ipg + parPrev->ar_cpg;
					/* Get first page after highest busy */
		vmAssert(VMTiledPg(ipgmaxprv));
					/* Must be tiled arena */
		if(ipgmaxprv < (VMAT_ST_laddrAbsMin >> PAGESHIFT))
		{
		    ipgmaxprv = (VMAT_ST_laddrAbsMin >> PAGESHIFT);
		}
					/* Don't go below absolute minimum
					   possible shared arena address */
		parNext->ar_ipgmax = ipgmaxprv;
					/* Lower the maximum size */
	    }
	    else if(parNext == parPrev) /* Else if arena is empty */
	    {
		vmAssert(FALSE);	/* This is not supposed to happen */

	    }
	}
    }
    else if(vmBdrySentAr(parPrev))
    {					/* Else if previous is boundary */
	ipgmaxprv = (parNext == parPrev)? parNext->ar_ipgmax: parNext->ar_ipg;
					/* Get lowest used shared page */
	if(ipgmaxprv > (VMAT_PT_laddrAbsMax >> PAGESHIFT))
	{
	    ipgmaxprv = (VMAT_PT_laddrAbsMax >> PAGESHIFT);
	}
				       /* Clamp to maximum allowed */
	parPrev->ar_cpg = ipgmaxprv - parPrev->ar_ipg;
					/* Base plus size equals maximum */
    }

    /*
     *	Remove AR from SGS control list if registered
     */

    if(par->ar_f & AR_SGS)
    {
	vmAssert(!(VMSysArenaPg(par->ar_ipg)) && !(VMShrArenaPg(par->ar_ipg)));
	/* Remove from SGS control list */
	vmSGSRemove(vmParToHar(par), VMGetPTDAAddr(par->ar_hobptda));
    }

    vmArFree(par);			/* Free the record */
}


/***EP	VMFreeMemOd - Free object or alias from context given subset of
 *		      object info.
 *
 *	This function frees an object or alias from a specified context
 *	given a subset of the object information buffer.  The following
 *	subsets are acceptable:
 *
 *	- base address of system arena object or alias,
 *	- base address of shared arena object or alias plus PTDA pseudo-handle
 *	  of context.  Shared arena aliases do not exist.
 *	- handle of system arena object
 *	- handle of shared or private arena object and PTDA pseudo-handle
 *	- complete information buffer filled and valid. (The caller must
 *	    not block between the time it has obtained object information
 *	    and this call)
 *
 *	WARNING: This function must remain in resident code, since some
 *	callers pass in the object information buffer without taking the
 *	object sempahore. Therefore this function must not be preempted
 *	from the start to vmFreeMemNotifyOd call.
 *
 *	ENTRY	fl			- Flags
 *
 *					  Exported flags:
 *
 *					  VMFM_NOLDR - no loader interaction
 *					  VMFM_HOB - object handle is specified
 *					  VMFM_GC - request performed from
 *					    garbage collection
 *					  VMFM_TASK_TERM - call is from task
 *					    termination
 *					  VMFM_VDM - call on behalf of VDM
 *					  VMFM_GC - call from Garbage Collection
 *					  VMFM_OI - Full object information
 *					  VMFM_OB_SEM - object sem. obtained
 *					    (sem count must be 1, will be
 *					     released upon return)
 *		pod			- Pointer to full or partial object
 *					  or alias data.  The handle
 *					  or base address must be filled in
 *					  and the PTDA handle must be given
 *					  for non-system arena objects.
 *
 *	RETURN	int			- Result code
 *					    NO_ERROR = success
 *					    non-zero = failure
 *					    NOTE: ERROR_INTERRUPT will not
 *					    be returned.  All free operations
 *					    ignore interrupts.
 */

int VMFreeMemOd(fl, pod)
ulong_t 		fl;		/* Flags */
register VMOD		*pod;		/* Pointer to object data buffer */
{
  ulong_t		cb;		/* Count of bytes */
  ulong_t		laddr;		/* Linear address */
  VMAR			*par;		/* Pointer to arena record */
  VMOB			*pob;		/* Pointer to object record */
  int			rc;		/* Return code */
// GSR start
  VMCO		      *pco;
  VMHCO 	      hco;
// GSR end

  if(fl & (VMFM_OD | VMFM_HOB))
    {
      pob = pod->od_pob;
    }

  /*
   *  If the object handle is specified, find the base address from the
   *  unique non-alias arena record.  Find the right hobPTDA and pPTDA. Skip
   *  all this if object info is already valid.
   */

  if(!(fl & VMFM_OD))
    {
      if(fl & VMFM_HOB)
	{
	  VMGetLaddr(VMHobOd(*pod), &(VMLaddrOd(*pod)));
	}
      if (VMSysArena(VMLaddrOd(*pod)) || VMPTDAOd(*pod) == NULL)
	{
	  VMPTDAOd(*pod) = pPTDACur;
	}
      VMLaddrOd(*pod) = VMRegionBase(VMLaddrOd(*pod));
    }

  /*
   *  Now we have od_laddr and od_pPTDA for all cases.	Round base address
   *  to page granularity.
   */

  laddr = VMLaddrOd(*pod);
  if(!VMSysArena(laddr))
    {
      /*
       *  BUGBUG - IDB 08/28/89 - The following fix is temporary.  It allows
       *  VMFreeMemOd to return ERROR_INVALID_ADDRESS when called to free
       *  private memory (user stacks) after the private arena has been erased.
       *  Thread 4 of sysinit task calls LDRFreeTask, which frees the private
       *  arena and its sentinel (via VMFreeTask).  Thread 4 then jumps to
       *  w_exit which calls tkFreeUserStacks, not being the final sysinit
       *  thread.
       */

      if((!VMShrArena(laddr)) &&
	    (VMPTDAOd(*pod))->ptda_ah.ah_parSen == NULL)
	{
	  return(ERROR_INVALID_ADDRESS);
	}

      /*
       *  An initialized VDM's address space may not be freed other
       *  than by the VDM itself.  This, together with the restriction
       *  that VDM's are single thread tasks, enables cross-object locking
       *  to be allowed for VDM's on their address space without the expense
       *  of having to take the semaphores for all of the objects involved
       *  in a cross object lock.
       */

      vmAssert(vmCurrTask(VMPTDAOd(*pod)) || !vmVDMTask(VMPTDAOd(*pod)));
    }


  /*
   *  Get the object semaphore and object information buffer notify the
   *  loader, shared memory records manager and/or debugger as
   *  appropriate to clean up loader data and debug aliases.  Clean
   *  up all non-debug aliases.  If the object is a resource object
   *  do not free it if the loader indicates that the object is still
   *  in use (by checking its BMP record reference count).
   */

  if((rc = vmFreeMemNotifyOd(SSToDS(&fl), pod)) != NO_ERROR)
    {
      /*
       *  If there is no object to free (e.g. this is a call gate) or
       *  ERROR_BUSY has been returned to indicate that the object is
       *  a special type for which reference counts not visible to
       *  VM are maintained (CS alias, named shared), return NO_ERROR
       *  and do not free the object.  Note that VMFreeMemNotify ALWAYS
       *  clears the object semaphore (if any) and the arena semaphore when
       *  returning a non-zero reply.
       */

      if(rc == ERROR_NO_OBJECT || rc == ERROR_BUSY)
	{
	  rc = NO_ERROR;
	}
      return(rc);
    }

  /*
   *  Disable bitmap bypass flag if enabled.
   */

  fl &= ~(VMFM_HOB | VMFM_OI);

  /*
   *  Free up any memory commitment for the given context
   */

  cb = VMSizeOd(*pod);

#if (PG_PRIV - vmfm_PAA_PRIV)
#    error "assertion failed"
#endif

#if (!(PG_PRIV - PG_SKIPFREE))
#    error "assertion failed"
#endif

#if (PF_LASTLDRCTXT - vmfm_LAST_LDR_CTXT)
#    error "assertion failed"
#endif

  /*
   *  Verify that there is an arena record, map to it, free
   *  the selectors in the specified process' context, if any.
   *  Clear out the selector from the arena record.
   */

  vmAssert(VMParOd(*pod) != NULL);
  par = VMParOd(*pod);

  if(VMParOd(*pod)->ar_f & AR_SELMAP)
    {
// GSR start - BUGBUG hard coded numbers
// KDH - Correct the checking of AR_HCO flag (Defect 156118).
       if ((par->ar_f & AR_HCO) && laddr < MAXGLOBALSHRLADDR && laddr >= MINGLOBALSHRLADDR)
       {
	   hco = VMHcoAr(*par);
	   pco = vmCoMap(hco);
	   if (pco->co_hconext != HCONULL) {
// GEB	       SELFree(pod);   // TRY TO FIX TWO APP LOAD PROBLEM
	       goto skip_pgfree;
	   }
       }
// GSR end
      SELFree(pod);
      if(VMSysArena(laddr))
	{
	  par->ar_sel = HARNULL;    /* clear sel/hobPTDA/hco field*/
	  par->ar_f &= ~AR_SELMAP;
	}
    }

  /*
   *  Call PGFree only if the object is not totally sparse
   */

  if(VMSizeOd(*pod) != -1)
    {
// GSR start
// KDH - Correct the checking of AR_HCO flag (Defect 156118).
       if ((par->ar_f & AR_HCO) && laddr < MAXGLOBALSHRLADDR && laddr >= MINGLOBALSHRLADDR)
       {
	   hco = VMHcoAr(*par);
	   pco = vmCoMap(hco);
	   if (pco->co_hconext != HCONULL) {
	       goto skip_pgfree;
	   }
       }
// GSR end
	rc = PGFree(laddr >> PAGESHIFT, VMPTDAOd(*pod),
		  (VMSizeOd(*pod) + PAGEMASK) >> PAGESHIFT,
		  (fl & (vmfm_LAST_LDR_CTXT | vmfm_PAA_PRIV)) | PG_SKIPFREE);

      /*
       *  The only error that could occur in PGFree is a swap in fail
       *  due to a bad sector on a disk (for example).	Temporarily force
       *  an internal error.
       */

	vmAssert(rc == NO_ERROR);
    }
skip_pgfree:;	 // GSR

  /*
   *  Find and free the context indicator.  This depends on the data
   *  type as follows.
   *
   *  For shared arena shared data - context record
   *	(plus arena and object records if last reference going away)
   *  For private arena shared data - arena record
   *	(plus object record if last reference going away)
   *  For private arena private data - arena and object records
   *  For system arena - arena and object records
   */

  vmFreeMemCtxtOd(pod);

  /*
   *  Free the object semaphore, free the object record if necessary
   *  and return.
   */

  VMClearSem(VMHobOd(*pod));
  pob = VMPobOd(*pod);
  if(pob->ob_har == HARNULL)
    {
      vmObFree(pob);
    }

  /*
   *  Free the arena semaphore.
   */

  VMClearSem(pod->od_pah->ah_hob);
  return(rc);
}


/***LP	vmFreeMemNotifyOd - Notify loader, debugger and free non-debug aliases
 *
 *	This function is called by VMFreeMemOd to obtain object semaphore
 *	and information buffer. It also cleans up loader and shared memory
 *	data and aliases to an object that is being freed.
 *
 *	WARNING: This function must remain in resident code
 *
 *	int vmFreeMemNotifyOd(ulong_t *pfl, VMOD *pod)
 *
 *	ENTRY		pfl		- Pointer to flags on entry:
 *					    VMFM_TASK_TERM
 *					      Task is being terminated
 *					    VMFM_HOB
 *					      Object handle specified
 *					    VMFM_OD
 *					      Object data buffer valid
 *					      on exit, the vmfm_LAST_LDR_CTXT
 *						may be set if we are freeing
 *						the last attachment to a loader
 *						managed object
 *			pod		- Pointer to where object data
 *					    will be returned
 *
 *	RETURN		int		- Result code (NO_ERROR = success)
 *
 *	EFFECTS The object's semaphore and the semaphore of the arena for the
 *		given object and context are both taken and only released if
 *		an error occurs.
 *
 *	PSEUDO CODE
 *
 *	for(;;) 	// Loop to get loader semaphore if needed
 *	  for(;;)	// Loop to remove debug aliases if any
 *	    if(system arena)
 *	      vmAssert(must not be VDM-reserved system arena area)
 *	    endif
 *	    Round base address to page granularity
 *	    if(not system arena address)
 *	      if(PTDA handle = HOBNULL, set to current PTDA)
 *	    endif
 *
 *	    do
 *	      Get the info for the object at the specified address
 *		in the specified context (VMGetOd)
 *	      if(no object)
 *		Locate the arena record via the bitmap/hash table
 *		free the arena record (VMVirtFree)
 *	      endif
 *	    while(trying to take semaphore without having to block
 *		    fails ERROR_BLOCKED)
 *
 *	    if((the object has aliases) and
 *	       (this VMFreeMem call is not for one of them))
 *	      if(the object has debug aliases)
 *		 Notify the debugger (vmDebugNotify)
 *
 *		 // Since notification will yield, we need to
 *		 // restart VMFreeMem from the beginning
 *
 *		 continue
 *	      else
 *		// There are non-debug aliases to be freed.  Free them all
 *		// and restart VMFreeMem from the beginning
 *		continue
 *	      endif
 *	    endif
 *	  }
 *
 *	// The object has no aliases (except possibly CS aliases)
 *
 *	if(object is shared)
 *	  if(MTE object)
 *	    if(last reference is going away)
 *	      if(we don't have the loader semaphore)
 *		if(the semaphore is available (LDRTestSem))
 *		  Take loader sem and remember that we have it (LDRGetSem)
 *		if(error) return(error code)
 *	      else
 *		Free object semaphore to avoid deadlock resulting
 *		  from waiting on the loader semaphore while we
 *		  hold the object semaphore (VMClearSem)
 *
 *		wait and take the loader semaphore
 *		  and remember that we have it (LDRGetSem)
 *
 *		// Since above may block, we need to repeat VMFreeMem
 *		// from the beginning to regain the object semaphore
 *		// while holding the loader semaphore (break takes us to
 *		// outer for loop).
 *
 *		continue
 *	      endif
 *	    endif
 *
 *	    // We hold the loader and object semaphores, the object
 *	    // has no aliases left and we have rechecked the reference
 *	    // count to ensure that the last reference is still going
 *	    // away.  Call the loader to clear out the handle from the
 *	    // MTE.
 *
 *	    rc = LDRFreeObject
 *	    Free the loader semaphore if we had it
 *
 *	  else	// Shared memory but not a loader object
 *	    if(VMNamedShrOb(pob))
 *	      rc = vmFreeNamedShr(object info, fl)
 *	    endif
 *	  endif
 *	else if(object is private arena private memory)
 *
 *	  // If not task termination and the object has aliases, they must be
 *	  // CS aliases.  Call the Selector Manager to update the CS aliases
 *	  // reference count and record if the data selector is being
 *	  // invalidated.  The Selector Manager retutns ERROR_BUSY if there
 *	  // are outstanding CS aliases.  We pass this back to VMFreeMem, to
 *	  // indicate that the object is not to be freed.  VMFreeMem converts
 *	  // this to NO_ERROR.
 *
 *	  if((not task termination) && (object has aliases))
 *	    rc = SelQueryFreeAlias(&object info)
 *	  endif
 *	}
 *
 *    return(rc)
 */

int   vmFreeMemNotifyOd(pfl, pod)
ulong_t 		*pfl;		/* Pointer to flags */
register VMOD		*pod;		/* Pointer to object data */
{
    /*
     *	BUGBUG IDB 07/12/90 - The VMOI declaration is temporary.  It should
     *	be removed after the remaining called routines have been converted
     *	to use VMOD.
     */

//  VMOI		oi;

    VMAR		*par;
    VMAR		*par2;
    VMOB		*pob;
    VMCO		*pco;
    VMCO		*pco2;
    ulong_t		flod;
    VMHOB		hobMTE;
    VMHOB		hob;
    VMHOB		hobOwner;
    struct ptda_s	*pPTDA;
    VMHCO		hco;
    VMHAR		har;
    VMAH		*pah;
    ulong_t		fl = *pfl;
    int 		rc = NO_ERROR;
    char		fbshrsem = 0;
    ulong_t		laddr = VMLaddrOd(*pod);

    /*
     *	Set up object info flags and save original address if any.
     */

    flod = NA;
    if(fl & VMFM_HOB)
    {
	flod |= VMOD_HOB;
    }

    /*
     *	If the target context is a running VDM, the free may only be
     *	performed by that VDM.
     */

    vmAssert((!(vmVDMTask(VMPTDAOd(*pod)))) || vmCurrTask(VMPTDAOd(*pod)));

    /*
     *	for loop to take loader semaphore.
     */

    for(;;)
    {
	/*
	 *  for loop to free debug aliases.
	 */

	for(;;)
	{
	    /*
	     * do loop to take object semaphore.
	     */

	    do
	    {
		/*
		 *  Get snapshot of object record information, skip if
		 *  we already have it upon entry.  Note that
		 *  the object may not be stable.  On the final iteration
		 *  of this loop, we will succeed in taking the object
		 *  semaphore without having to block, so the last snapshot
		 *  we take will remain up-to-date.
		 */

	    getod:

		if(!(fl & VMFM_OD))
		{
		    if(((rc = VMGetOd(flod, pod)) != NO_ERROR) &&
		       (rc != ERROR_NO_OBJECT))
		    {
			break;
		    }
		}
		else if(pod->od_pob == NULL)
		{
		    rc = ERROR_NO_OBJECT;
		}

/*
 *  BUGBUG IDB 07/12/90 - The following conversion is temporary.  It should
 *  be removed when the oi buffer is no longer used.
 */
//		VMOdToOi(pod, SSToDS(&oi));

		/*
		 *  Copy frequently used items onto the stack.	For
		 *  objectless items, skip object record items.
		 */

		par = VMParOd(*pod);
		pah = VMPahOd(*pod);
		if(rc != ERROR_NO_OBJECT)
		{
		    hob = VMHobOd(*pod);
		    hobOwner = VMHobOwnerOd(*pod);
		    hobMTE = VMHobMTEOd(*pod);
		}
		pPTDA = VMPPTDAOd(*pod);

		/*
		 *  Clear VMFM_OD flag, so that we will refresh the object info
		 *  if we ever have to block.
		 */

		fl &= ~VMFM_OD;

		if(rc != NO_ERROR)
		{
		    if(rc == ERROR_NO_OBJECT)
		    {
			/*
			 *  The given address and context is mapped by an
			 *  objectless arena record.  In this case, the
			 *  object description buffer contains an arena
			 *  record pointer/handle.
			 */


			/*
			 *  VDM tasks have objectless uvirt arena
			 *  records. Physical pages for these must
			 *  also be freed. During Task termination
			 *  VDM tasks are not marked, therefore
			 *  vmVDMTask(pPTDA) fails even if the task
			 *  is really a VDM task.
			 */

			rc = PGFree(par->ar_ipg, VMPTDAOd(*pod),
				par->ar_cpg, PG_SKIPFREE);

			if(rc != NO_ERROR)
			{
			    rc = ERROR_INVALID_ADDRESS;
			    break;
			}

			/*
			 *  If the objectless address space
			 *  is either not reserved for initial load of a
			 *  huge item or reload of an MTE object, or this
			 *  is a special call from the loader's garbage
			 *  collection to dereserve such address space,
			 *  propagate ERROR_NO_OBJECT back up to VMFreeMem.
			 */

			if((fl & VMFM_GC) || vmVDMTask(pPTDA) ||
			  (VMPrvArenaOd(*pod) && (fl & VMFM_TASK_TERM)))
			{

			    /*	Objectless arena records in a VDM task
			     *	also have alias records attached to them.
			     */
			    if (par->ar_hal != HALNULL)
			    {
				/*
				 *  No need to check for VMHAL_PRIV, since
				 *  objectless arena records can not be
				 *  privatized.
				 */

				vmAssert (par->ar_hal != VMHAL_PRIV);
				BMPFree(palVMAliases, vmHalToPal(par->ar_hal));
			    }

			    while(VMGetSem(pah->ah_hob,
				  VMGS_TAKESEM | VMGS_IGNINT | VMGS_PSEUDO,
				  NULL ) == ERROR_INTERRUPT);
			    VMVirtFree(par, pah);
			    VMClearSem(pah->ah_hob);
			    rc = ERROR_NO_OBJECT;
			}
			else
			{
			    rc = ERROR_INVALID_ADDRESS;
			}
		    }
		}

		/*
		 *  If any error has occurred, clear out semaphores and return.
		 */

		if(rc != NO_ERROR)
		{
		    break;
		}

		/*
		 *  Validate parameters.  The address given must, when rounded
		 *  down to a page boundary, base exactly match an alias or
		 *  the entire object.	Obtain rounded address and byte counts.
		 *  If we are passed an address that doesn't point to
		 *  beginning of an object, od_od_laddr will have been changed
		 *  by VMGetOd.
		 */

		if((laddr - VMLaddrOd(*pod)) >> PAGESHIFT)
		{
		    rc = ERROR_INVALID_ADDRESS;
		    break;
		}

		/*
		 *  Fail if there are outstanding long-term locks placed
		 *  by this task via addresses within the specified object
		 *  or alias.  If there are outstanding long-term locks
		 *  placed via other tasks or via other addresses only,
		 *  we allow the VMFreeMem request since it will not affect
		 *  those locks.
		 */

		if((pod->od_pob)->ob_cllock != 0)
		{
		    if((rc = vmAccessLongLockInfo(VMHarOd(*pod),
					    (pod->od_pPTDA)->ptda_handle,
					    vmlt_CHECK)) == ERROR_LONGLOCK)
		    {
			break;
		    }
		}

		/*
		 *  If the memory is named shared memory, we must obtain
		 *  shared memory semaphore before obtaining the object
		 *  semaphore and the loader semaphore.  Take the named
		 *  shared semaphore ignoring interrupts.  Internal error
		 *  if some other error occurs.  Remember that semaphore
		 *  has been taken.
		 */

		if((hobOwner == MSHAREOWNER) && !(fl & vmfm_SHR_SEM)
		    && !VMIsAliasOd(*pod))
		{
		    while ((rc = KSEMRequestExclusive(vmNamShrSem, -1))
			== ERROR_INTERRUPT);
		    vmAssert(rc == NO_ERROR);
		    fbshrsem++;
		    fl |= vmfm_SHR_SEM;

		    /*
		     *	We may have blocked while obtaining the shared
		     *	memory semaphore. So go back to calling VMGetOd
		     *	once again.
		     */

		    goto getod;
		}
	    }
	    while((rc = VMGetSem(VMHobOd(*pod),VMGS_TAKESEM |
	      VMGS_SLOCKWAIT | VMGS_ERRBLOCK,SSToDS(&pob))) == ERROR_BLOCKED);
					/* Loop while we would need to block */

	    /*
	     *	Break out of alias for loop if an error has occurred.
	     */

	    if(rc != NO_ERROR)
	    {
		break;
	    }

	    fl |= vmfm_OB_SEM;

	    /*
	     *	Get the semaphore for the arena to which this object belongs.
	     *	Ignore interrupts since we are freeing resources.  No error
	     *	should be possible, since we successfully got the object's
	     *	semaphore (the arena cannot have gone away).
	     */

	    rc = VMGetSem(VMPahOd(*pod)->ah_hob,
			  VMGS_IGNINT | VMGS_TAKESEM | VMGS_PSEUDO, NULL);
	    vmAssert(rc == NO_ERROR);
	    fl |= vmfm_AH_SEM;

	    /*
	     *	Check for aliases. If the alias itself is being freed, no
	     *	special treatment, but if the object itself is being freed
	     *	and it has aliases, the aliases should also be freed.  If the
	     *	object has debugger aliases in the region being freed, then
	     *	release the semaphore, notify the debugger process and restart
	     *	the free operation.
	     */

	    /*
	     *	WARNING:vmHasDebugAlias changes the contents of oi if it finds
	     *	an alias. So save necessary information to restart
	     *	if necessary. od_laddr is already saved in p, od_hob will
	     *	not be changed, so we need to save od_pPTDA.
	     */

	    if(VMHasAliasOd(*pod) && !(VMIsAliasOd((*pod))))
	    {
		/*
		 *  Object has aliases and not just freeing one of them.
		 */

//		if(vmHasDebugAlias(SSToDS(&oi)))
		if(vmHasDebugAlias(pod))
		{			/* If there are debug aliases */
		    /*
		     *	BUGBUG IDB 07/12/90 - The following conversion is
		     *	temporary.  It should be removed when the oi buffer is
		     *	no longer used.
		     */

//		    VMOiToOd(SSToDS(&oi), pod);
		    VMClearSem(hob);
					/* Release the object semaphore */
		    VMClearSem(pah->ah_hob);
					/* Release the arena semaphore */
		    fl &= ~(vmfm_OB_SEM | vmfm_AH_SEM);
					/* Indicate semaphores not obtained */
#ifdef INCDEBUG
		    DbgAliasFree((char *) (VMLaddrOd(*pod)),
			(short) VMHobPTDAOd(*pod));
					/* Notify the debugger */
#endif /* DEBUG */
		    /*
		     *	vmHasDebugAlias changes the contents of poi. We may
		     *	have to reset this in order to start over again.
		     *	Hob doesn't change for an alias, but the address
		     *	and the context of the alias is different.  Reset
		     *	the context and also the address if generating object
		     *	data from the address.
		     */

		    VMPTDAOd(*pod) = pPTDA;
		    if (!(flod & (VMOD_HOB | VMOD_OD)))
		    {
			VMLaddrOd(*pod) = laddr;
		    }

		    continue;		/* Restart after yield */
		}

		/*
		 *  There are non-debug aliases that need to be freed.	Note
		 *  that we must have the object semaphore when freeing an
		 *  object's aliases.  Also, CS aliases are not freed.
		 */

		vmFreeObjectAliases(hob, pPTDA->ptda_handle);

		/*
		 *  Update the object data after freeing the
		 *  non-debug non-CS aliases.  Note that since we have the
		 *  object's semaphore, the freeing of these aliases
		 *  is the only change that can have occurred.	Note also
		 *  that we cannot perform the update by resuming
		 *  vmFreeMemNotify from the beginning since we would get
		 *  an infinite loop if we still have CS aliases.
		 */

		rc = VMGetOd(VMOD_HOB, pod);
/*
 *  BUGBUG IDB 07/12/90 - The following conversion is temporary.  It should
 *  be removed when the oi buffer is no longer used.
 */
//		VMOdToOi(pod, SSToDS(&oi));
		vmAssert(rc == NO_ERROR);
	    }

	    /*
	     *	All aliases (except CS aliases) are freed.  Break out of
	     *	loop.
	     */

	    break;
	}			      /* end alias for loop */

	/*
	 *  If an error has occurred, break out of loader semaphore-taking
	 *  for loop.
	 */

	if(rc != NO_ERROR)
	{
	    break;
	}

	/*
	 *  Get frequently used object data into local variables.
	 */

	hob = VMHobOd(*pod);
	hobOwner = VMOwnerOd(*pod);
	hobMTE = VMMTEOd(*pod);
	pco = VMPcoOd(*pod);
	par = VMParOd(*pod);
	pPTDA = VMPTDAOd(*pod);

	if((hobOwner == MSHAREOWNER) && !VMIsAliasOd((*pod)))
	{
	    /*
	     *	Named shared memory. We must have already obtained the named
	     *	shared semaphore. Call vmFreeNamedSharedMem, and continue if
	     *	NO_ERROR is returned.  Clear the named shared memory semaphore
	     *	if we got the semaphore in this routine, i.e. this call did
	     *	not originate from a VMAlloc cleanup.
	     */

	    vmAssert((fl & vmfm_SHR_SEM) &&
		     VMShrContOd(*pod) && VMShrArena(laddr));
	    if (fbshrsem)
	    {
//		rc = vmFreeNamedSharedMem(fl, SSToDS(&oi));
		rc = vmFreeNamedSharedMem(fl, pod);
		KSEMRelease(vmNamShrSem);
		fbshrsem = 0;			//127663
	    }
	}
	else if(VMShrContOd(*pod))
	    /* If shared contents (shr or prv arena) */
	{
	    /*
	     *	If the object belongs to an MTE, is shared, and we are
	     *	not freeing an alias, we must take the following special action:
	     *
	     *	If the VMFM_NOLDR flag is specified, no special loader
	     *	interaction is required.  The loader specifies this flag
	     *	when the VMFreeMem call is being made on behalf of a
	     *	DOSFREEMODULE call for a resource object.  In this case,
	     *	the resource object reference count will have been reduced to
	     *	zero by the loader (DOSFREEMODULE nullifies the effect of
	     *	DOSGETRESOURCE).
	     *
	     *	If the VMFM_NOLDR flag is not specified, special loader
	     *	interaction is required.  If the last attachment to the object
	     *	is being severed, or the object is a resource object, the
	     *	loader must be notified to update MTE/resource counts and
	     *	to determine whether the free should take place.
	     *	For non-resource objects, the loader will zeroise the handle
	     *	in the object's MTE so that a subsequent attachment to the
	     *	MTE will cause the object to be reloaded.  For resource objects,
	     *	the loader is always called (whether or not there are other
	     *	processes still attached).  The loader will decrement the
	     *	reference counts in the local and global BMP records for the
	     *	resource object.  If the VMFreeMem call is being made within
	     *	task termination, the loader decrements the global reference
	     *	count by the outstanding local reference count.  The loader
	     *	returns ERROR_BUSY if the object should not be freed.
	     *
	     *	Loader must not be notified for privatized contexts, because
	     *	this notification is done at the time of privatization.
	     */

	    if(!VMApiOd(*pod) &&
		hobMTE != HOBNULL &&
	       !vmSystemOwner(hobMTE) &&
	       !(VMIsAliasOd(*pod)))
	    {
		/*
		 *  Object is a shared loader object and we are not freeing an
		 *  alias.
		 *  Must be at least one (possibly privatised) context
		 *  attached to it.  If we are freeing the last context, set
		 *  the vmfm_LAST_LDR_CTXT flag.  Note that this flag is used
		 *  by the Page Manager later in addition to use here.
		 */

		/* In order for Page Manager to do discardable memory
		 * overcommit correctly, we need to pass the flag PG_PRIV
		 * to PGFree in the following instances:
		 * * Either we are freeing the last shared context in an
		 *    object with privatized contexts.
		 * * Or all shared contexts are gone, and we are freeing
		 *    one of the privatized contexts.
		 */

		if (VMSearchRef(VMSR_TWO, hob) == ERROR_INVALID_DATA)
		{
		    /* There is only one or no shared contexts. See if we are
		     * freeing that shared one.
		     */

		    if (!vmPrivAr(par) &&
			!((par->ar_f & AR_HCO) && (hob != HOBNULL) &&
			(vmPrivCo(pco))))
		    {
			/* We are freeing the last shared context */

			fl |= vmfm_LAST_LDR_CTXT;

			/* See if there are privatized contexts */

			if (par->ar_f & AR_HCO)
			{
//			    for(hco = par->ar_hco; hco; hco = pco2->co_hconext)
			    for(hco = VMHcoAr(*par); hco; hco = VMHconextCo(*pco2))
			    {
				pco2 = vmCoMap(hco);
				if (vmPrivCo(pco2))
				{
				    fl |= vmfm_PAA_PRIV;
				    break;
				}
			    }
			}
			else
			{
			    for(har = pob->ob_har; har ; har = par2->ar_harlink)
			    {
				par2 = vmArMap(har);
				if (vmPrivAr(par2))
				{
				    fl |= vmfm_PAA_PRIV;
				    break;
				}
			    }
			}
		    }
		    else
		    {
			/* We are freeing a privatized context, see if there
			 * are other shared contexts
			 */
			if (VMSearchRef(VMSR_ONE, hob)
			   == ERROR_INVALID_DATA)
			{
			    fl |= vmfm_PAA_PRIV;
			}
		    }
		}

		/*
		 *  Check that arena record is not objectless.	This
		 *  must be true since we checked for shared content using
		 *  VMShrOi macro earlier.  Therefore, if AR_HCO is set in the
		 *  arena record flags, there must be a non-null context record
		 *  chain.
		 */

		vmAssert(hob != HOBNULL);
		vmAssert((par->ar_f & AR_HCO) ||
		  vmPcoToHco(VMPcoOd(*pod) != HOBNULL));

		/*
		 *  If loader interaction is not disabled, and the object
		 *  is not privatized in the target
		 *  context, and is either a resource object or is addressable
		 *  in only one remaining context, call the loader to update
		 *  its MTE.
		 */

		if((!(fl & VMFM_NOLDR)) &&

		   !(fl & vmfm_PAA_PRIV) &&

		   ((LDRIsResource(hob,
			    hobMTE,
			    pPTDA->ptda_rsrclist) == NO_ERROR) ||
			    (fl & vmfm_LAST_LDR_CTXT)))
		{
		    /*
		     *	Request is to detach the last non-privatized context
		     *	of a shared content object, or the loader indicates
		     *	that object is a resource object and is can be freed
		     *	because there are no outstanding DOSGETRESOURCE calls
		     *	in effect.  Unless specifically requested not to do
		     *	so (VMFM_NOLDR flag), call the loader to update its
		     *	MTE.  Loader zeroises object's handle in the MTE so
		     *	that a subsequent attach to the object will cause
		     *	the object to be reloaded.  Since the updating of the
		     *	MTE requires the loader semaphore and we already hold
		     *	the object's handle semaphore (which is lower in the
		     *	semaphore hierarchy) we must avoid requesting the
		     *	former if we would have to block.  If the loader
		     *	semaphore is busy, clear the handle semaphore
		     *	and arena semaphore and repeat VMFreeMemNotify from
		     *	the beginning.
		     */

		    if(!(fl & vmfm_LDR_SEM))	  /* if we don't have */
		    {			      /* the loader sem4 */
			if(!LDRTstnSetSem())		/* if s4 not free */
			{
			    /*
			     *	We should already have the handle and arena
			     *	semaphores.  Check this, then release them.
			     */

			    vmAssert((fl & vmfm_OB_SEM) && (fl & vmfm_AH_SEM));
			    VMClearSem(hob);
			    VMClearSem(pah->ah_hob);
			    fl &= (~(vmfm_AH_SEM | vmfm_OB_SEM));

			    /*
			     *	Now take the loader semaphore.	We may block,
			     *	but we ignore interrupts since this is a free
			     *	resource path.	No other error should occur.
			     *	Record that we have the semaphore and repeat
			     *	this routine.
			     */

			    while((rc = LDRGetSem()) == ERROR_INTERRUPT);
			    vmAssert(rc == NO_ERROR);
			    fl &= ~vmfm_LAST_LDR_CTXT;
			    fl |= vmfm_LDR_SEM;
			    continue;
			}

			/*
			 *  Remember that we have taken the loader semaphore.
			 */

			fl |= vmfm_LDR_SEM;
		    }

		    /*
		     *	Notify the loader of free on resource object or
		     *	detach from last shared context on shared object.
		     *	At this point there may still be privatized contexts.
		     *	Loader tells us whether to free object's memory.
		     */

		    par = vmArMap(pob->ob_har);
		    rc = LDRFreeObject(hobMTE,
				       hob,
				       fl & VMFM_TASK_TERM,
				       pPTDA);
		}
	    }
	}
	else   /* Private data (shr or prv arena) or system arena */
	{
	    /*
	     *	At this stage, we have already freed up all aliases except
	     *	CS aliases.  If the object still has aliases, they must
	     *	be CS aliases.	If the target task is not terminating we
	     *	must call the Selector Manager to update the CS alias
	     *	information.  If the request is to free the object
	     *	itself, the Selector Manager will keep the CS aliases
	     *	but mark the CS alias record to record that the
	     *	data selector has gone away.  If the request is to
	     *	free the CS alias, the Selector Manager decrements
	     *	the reference count in the CS alias record.  In
	     *	the latter case, the Selector manager returns a reply
	     *	ERROR_BUSY if there are still outstanding CS alias
	     *	references, and VMFreeMem does not free the virtual
	     *	memory mapped by the CS alias.	If the target task
	     *	is terminating, we go ahead and free the virtual memory
	     *	because CS aliases are only created in the context of
	     *	the terminating task.
	     */

	    if(!(fl & VMFM_TASK_TERM) &&
	      !VMSysArena(laddr) &&
	      VMPrvContOd(*pod) &&
	      VMHasAliasOd(*pod))
	    {
//		rc = SELQueryFreeAlias(SSToDS(&oi));
		rc = SELQueryFreeAlias(pod);
	    }
	}

	break;
    }					      /* End loader semaphore loop */

    /*
     *	Free the loader semaphore if we had it.  Unlike the other two
     *	semaphores, it is no longer needed.
     */

    if(fl & vmfm_LDR_SEM)
    {
	LDRClearSem();
    }

    /*
     *	If an error occurred, release the arena and object semaphores if we
     *	had taken them.  In the no-error case, VMFreeMemOd will release them.
     */

    if(rc != NO_ERROR)
    {
	if(fl & vmfm_OB_SEM)
	{
	    VMClearSem(hob);
	}

	if(fl & vmfm_AH_SEM)
	{
	    VMClearSem(pah->ah_hob);
	}
							 //127663
	if (fbshrsem)					 //127663
	{						 //127663
	    // D147433 - Commented out line
	    // vmFreeNamedSharedMem(fl, pod);		  D147433 - 127663
	    KSEMRelease(vmNamShrSem);			 //127663
	}						 //127663
    }
    *pfl = fl;
    return(rc);
}


/***LP	vmFreeMemCtxtOd - Find and free context indicator for object
 *
 *	This function is called by VMFreeMem to free the context
 *	indicator for an object.  This depends on the data type as follows:
 *
 *	For shared arena shared data - context record
 *	  (plus arena and object records if last reference going away)
 *	For private arena shared data - arena record
 *	  (plus object record if last reference going away)
 *	For private arena private data - arena and object records
 *
 *	If the last reference to an MTE object in the shared arena is going
 *	away, the arena record is retained so that the address space remains
 *	available because subsequent reattachments to the MTE will cause the
 *	object to be reloaded and this load must use the same virtual addresses
 *	in order to preserve the validity of fixups from other modules.
 *
 *	The object's semaphore must be held by the calling thread
 *	before calling this routine.
 *
 *	WARNING: This functon must remain in resident code.
 *
 *	void vmFreeMemCtxtOd(VMOD *pod)
 *
 *	ENTRY		pod		- Object data buffer pointer
 *
 *	RETURN				None
 *
 *	CALLS	vmArMap, vmCoMap, vmCoFree, VMShrArenaOd,
 *		vmHandleMap, VMVirtFree, vmFreeAlias
 */

void	    vmFreeMemCtxtOd(pod)
register VMOD		*pod;		/* Pointer to object details */
{
  register VMAR *par;		/* Pointer to arena record */
  VMAR		*parPrev;	/* Pointer to previous arena record */
  VMCO		*pco;		/* Pointer to context record */
  VMCO		*pcoPrev;	/* Pointer to previous context record */
  register VMOB *pob;		/* Pointer to object record */
  VMOB		*pob2;
  VMOB		*pobPrev;	/* Pointer to previous object record */
  VMHCO 	hco;		/* Context record handle */
  ulong_t		fl = 0; 	/* Internal flags */

  par = VMParOd(*pod);		/* Point to arena record */
  pob = VMPobOd(*pod);		/* Point to object record */

  vmSemAssertOb(pob);			/* Must have object semaphore */
  if(VMShrArenaOd(*pod))
    {					/* If freeing in shared arena */
      vmAssert(!VMIsAliasOd(*pod));
					/* Alias cannot be in shared arena */
      /*
       *  We will locate and free VM records depending on the type of data
       *  as follows.
       *
       *  Arena  Data Type	Last Reference
       *
       *  System	 NA		NA		    AR, OB
       *  Shared	 Shared 	NO		CO
       *  Shared	 Shared non-MTE YES		CO, AR, OB
       *  Shared	 Shared MTE	YES		CO,	OB
       *  Shared Arena Priv		NO			OB
       *  Shared Arena Priv	non-MTE YES		    AR, OB
       *  Shared Arena Priv	MTE	YES			OB
       *  Priv	 Shared 	NO		    AR
       *  Priv	 Shared 	YES		    AR, OB
       */

      if(VMParOd(*pod)->ar_f & AR_HCO)/* If there is a context record */
	{
	  vmAssert(VMShrContOd(*pod)); /* Must be shared */
					/* Should have record handle */
	  pco = VMPcoOd(*pod); /* Point at context record */
	  vmAssert(pco != NULL);
	  hco = vmPcoToHco(pco);
	  vmAssert(pco->co_hobptda == VMPTDAOd(*pod)->ptda_handle);
				/* Owner field must match */
//	  if(par->ar_hco != hco)
	  if(VMHcoAr(*par) != hco)
	    {				/* If not first in list */
//	      vmAssert(par->ar_hco != HCONULL);
	      vmAssert(VMHcoAr(*par) != HCONULL);
					/* Must have link */
//	      pcoPrev = vmCoMap(par->ar_hco);
	      pcoPrev = vmCoMap(VMHcoAr(*par));
					/* Map first in list */
//	      while(pcoPrev->co_hconext != hco)
	      while(VMHconextCo(*pcoPrev) != hco)
	      { 			/* While match not found */
//		  vmAssert(pcoPrev->co_hconext != HCONULL);
		  vmAssert(VMHconextCo(*pcoPrev) != HCONULL);
					/* Must have a link */
//		  pcoPrev = vmCoMap(pcoPrev->co_hconext);
		  pcoPrev = vmCoMap(VMHconextCo(*pcoPrev));
					/* Point to next record */
	      }
//	      pcoPrev->co_hconext = pco->co_hconext;
	      VMCoHconext(*pcoPrev, VMHconextCo(*pco));
					/* Delete record from list */
	    }
	  else
	  {
//	      par->ar_hco = pco->co_hconext;
	      VMArHco(*par, VMHconextCo(*pco)); /* Delete 1st record in list */
	  }

	  /*
	   *  If the last context attached to this shared arena shared
	   *  data object is going away, indicate that the object record
	   *  is to be freed by setting the ob_har field to null.  If the
	   *  address space does not need to be retained for possible
	   *  re-attaches to an MTE object, set flag to indicate that the
	   *  arena record is also to be freed.
	   */

//	  if(par->ar_hco == HCONULL)
	  if(VMHcoAr(*par) == HCONULL)
	    {				/* If last context going away */
	      par->ar_hob = HOBNULL;	/* Arena rec no longer has object rec */
	      pob->ob_har = HOBNULL;	/* indicate to free object record */
	      if((fl & VMFM_GC) || !vmReloadAr(par))
		{			/* else free arena record */
		  fl |= vmfm_FREE_AR;
		}
	    }
	  pco->co_hobptda = HOBNULL;	/* Clear out PTDA handle for BMP busy */
	  vmCoFree(pco);		/* Free the context record */
	}
      else				/* Shared arena private data */
	{
	  vmAssert(!(par->ar_f & AR_HCO) && !VMShrContOd(*pod));
					/* Must not have link list */
					/* Must not be shared */
	  if(par->ar_hob != VMHobOd(*pod))
	    {				/* If not first in list */
	      pobPrev = vmHandleMap(par->ar_hob);
					/* Map first in list */
	      while(pobPrev->ob_hobnext != VMHobOd(*pod))
		{			/* While match not found */
		  vmAssert(pobPrev->ob_hobnext != HOBNULL);
					/* Must have a link */
		  pobPrev = vmHandleMap(pobPrev->ob_hobnext);
					/* Point to next record */
		}
	      pobPrev->ob_hobnext = pob->ob_hobnext;
					/* Delete record from list */
	    }
	  else par->ar_hob = pob->ob_hobnext;
					/* Delete first record from list */
	  /*
	   *  If the last context attached to this shared arena instance
	   *  data object is going away, indicate that the object record
	   *  is to be freed by setting the ob_har field to null.  If the
	   *  address space does not need to be retained for possible
	   *  re-attaches to an MTE object, set flag to indicate that the
	   *  arena record is also to be freed.
	   */

	  if(par->ar_hob == HOBNULL)
	    {				/* If last context going away */
	      if((fl & VMFM_GC) || !vmReloadAr(par))
		{			/* else free arena record */
		  fl |= vmfm_FREE_AR;
		}
	    }
	  if(!(vmHasAliasOb(pob)))
	    {				/* Remove object only if there are
					 * no other aliases left */
	      pob->ob_har = HARNULL;	/* Detach from arena record */
	    }
	  else				/* CS aliases remaining */
	    {
	      parPrev = vmArMap(pob->ob_har);
	      vmAssert(vmAliasAr(parPrev));
	      vmAssert(parPrev->ar_harlink == VMHarOd(*pod));
	      parPrev->ar_harlink = HARNULL;
	    };
	}
    }
  else					/* System or private arena */
    fl |= vmfm_FREE_AR; 		/* Indicate to free the ar */

  /*
   *	Remove the arena record from harlink list if it needs to
   *	be freed, and free it.
   */

  if(fl & vmfm_FREE_AR)
    {
      if(par->ar_hob != HOBNULL)
	{
	  pob2 = vmHandleMap(par->ar_hob);
					/* != hob in instance data case */
	  if(pob2->ob_har != VMHarOd(*pod))
	    {				/* If not first in list */
	      parPrev = vmArMap(pob2->ob_har);
					/* Map first in list */
	      while(parPrev->ar_harlink != VMHarOd(*pod))
		{			/* while match not found */
		  vmAssert(parPrev->ar_harlink != HARNULL);
					/* Must have a link */
		  parPrev = vmArMap(parPrev->ar_harlink);
					/* Point to next record */
		};
	      parPrev->ar_harlink = par->ar_harlink;
	    }
	  else				/* Remove first one in the list */
	    pob2->ob_har = par->ar_harlink;
	}
      par->ar_harlink = HARNULL;

      /*
       *  If alias, clean up alias records
       */

      if(vmAliasAr(par))
	{
	  vmFreeAlias(VMHarOd(*pod),VMHobOd(*pod));
	}
      VMVirtFree(par, VMPahOd(*pod));
					/* Free the virtual memory */
    }

  /*
   *  If the object is being freed, then make its object record
   *  handle unmappable.
   */

  if(pob->ob_har == HARNULL)		/* If object is being freed */
    {
      pob->ob_hobowner = VMDEADOWNER;/* Make object record unmappable */
    }
  return;
}					/* end free virtual memory */




/***LP	vmSetObPermOd - Set default per-context object permissions
 *
 *	This procedure replaces the default per-context object access
 *	permissions (page manager PG_R | PG_W | PG_X | PG_U | PG_GUARD
 *	flags) as stored in the appropriate data structure (arena
 *	record, object record, or context record) with the specified
 *	value.
 *
 *	vmSetObPermOd (pod, fl)
 *
 *	ENTRY	pod		- pointer to object descriptor structure
 *		fl		- access permission flags (PG_?)
 *
 *	RETURN	none
 *
 *	WARNING This routine is not meant to be used at allocation time,
 *		but rather only when the permissions should be updated
 *		(currently only when an object is attached multiple times
 *		to a context).
 *
 *	EFFECTS
 */

void   vmSetObPermOd(pod, fl)
register VMOD *pod;
ulong_t fl;
{
    vmSemAssertOb(pod->od_pob);

    vmAssert((fl & ~(PG_R | PG_W | PG_X | PG_U | PG_GUARD)) == 0);

    if(!VMShrArenaPg((pod->od_par)->ar_ipg))
    {
	/*
	 *  Per-context object permissions are stored in the arena
	 *  record for all objects except those in the shared arena.
	 */

	(pod->od_par)->ar_f = ((pod->od_par)->ar_f & ~AR_PERMMASK) |
				  (fl << AR_PERMSHFT);
    }
    else if((pod->od_par)->ar_f & AR_HCO)
    {
	/*
	 *  Shared arena, shared content.  The permission flags are
	 *  stored in the context record.
	 */
	(pod->od_pco)->co_fb = (uchar_t)
			       (((pod->od_pco)->co_fb & ~CO_PERMMASK) |
				    (fl >> CO_PERMSHFT));
    }
    else
    {
	/*
	 *  Shared arena, private content.  The permission flags are
	 *  stored in the object record.
	 */
	(pod->od_pob)->ob_fs = (ushort_t)
			       (((pod->od_pob)->ob_fs & ~OB_PERMMASK) |
				   (fl << OB_PERMSHFT));
    }
}


/**************************************************************************
 *									  *
 *  WARNING: Everything below this line will be in swappable code!!!!	  *
 *									  *
 **************************************************************************/

#pragma code_seg("SWAP32_1","CODE")


/***EP	VMGetObPermOd - Get default per-context object permissions
 *
 *	This procedure returns the default per-context object access
 *	permissions (page manager PG_R/PG_W/PG_X/PG_U/PG_GUARD
 *	flags) as stored in the appropriate data structure (arena
 *	record, object record, or context record).
 *
 *	WARNING: This function is in swappable code!  Object semaphore
 *	must be obtained before calling this function.
 *
 *	ulong_t VMGetObPermOd(pod)
 *
 *	ENTRY		pod		- Pointer to object data
 *
 *	RETURN		ulong_t 	- Access permissions
 */

ulong_t VMGetObPermOd(pod)
register VMOD		*pod;		/* Pointer to object info */
{
  vmSemAssertOb(pod->od_pob);

  if(!VMShrArena(pod->od_laddr))
    {
      /*
       *  Per-context object permissions are stored in the arena
       *  record for all objects except those in the shared arena.
       */

      return((pod->od_par->ar_f & AR_PERMMASK) >> VMAC_PSHIFT);
    }
  else if(VMShrContOd(*pod))
    {
      /*
       *  Shared arena, shared content.  The permission flags are
       *  stored in the context record.
       */

      vmAssert(pod->od_par->ar_f & AR_HCO);
      return((pod->od_pco->co_fb & CO_PERMMASK) << CO_PERMSHFT);
    }
  else
    {
      /*
       *  Shared arena, private content.  The permission flags are
       *  stored in the object record.
       */

      return((pod->od_pob->ob_fs & OB_PERMMASK) >> OB_PERMSHFT);
    }
}


/***EP	VMSetMem - Commit/Decommit/Set Protection for Region
 *
 *	This routine performs one of three functions:
 *
 *	Commit memory resources for region
 *	Decommit memory resources for region
 *	Set protection for memory region
 *
 *	The region must belong to a single memory object (except for VDMs).
 *	If the memory resource is shared, the effect of commit or decommit
 *	is global, i.e. it affects all contexts that have access, whether
 *	by alias or not.  Attempts to commit memory that is already committed
 *	will return error.
 *	WARNING: This procedure is in swappable code
 *
 *	VMSetMem (p, cb, hobPTDA, fl, blkno)
 *
 *	ENTRY	p		- region's virtual address
 *		cb		- region's size in bytes
 *		hobPTDA 	- PTDA handle
 *		fl		- flags
 *				  VMS_COMMIT
 *				    Commit memory resources
 *				    NOTE:  The Page Manager storage
 *					   type and state must also be
 *					   specified using the appropriate
 *					   PG_x and PAS_y flags, see
 *					   pgexport.h.
 *				  VMS_DECOMMIT
 *				    Decommit memory resources
 *				  VMS_DFLTPERM
 *				    Set access permissions to the default
 *				    value (initial).
 *				  VMS_INCPERM
 *				    Increase permissions by the specified
 *				    values, ie. "OR" the specified permissions
 *				    into the existing permissions.  This also
 *				    affects the object's per-context "default"
 *				    permissions.
 *				  PG_SKIPFREE
 *				    Ignore non-committed pages when setting
 *				    access permissions or decommitting.  If
 *				    not specified, attempting to decommit or
 *				    set access permissions on uncommitted memory
 *				    will return error.
 *				  PG_EXECUTE
 *				    Execute access to the committed range
 *				    is desired
 *				  PG_READ
 *				    Read access to the committed range
 *				    is desired
 *				  PG_WRITE
 *				    Write access to the committed range
 *				    is desired
 *				  PG_GUARD
 *				    Access to the committed range of pages
 *				    is to cause a "guard page entered"
 *				    condition in the subject process.
 *			blkno	- loader block number.	This is relevant
 *				  only to loader-originated VMSetMem's
 *				  for discardable objects.
 *
 *	RETURN	int		- return code (NO_ERROR if successful)
 */

int		VMSetMem(p, cb, hobPTDA, fl, blkno)
ulong_t 	p;
ulong_t 	cb;
VMHOB		hobPTDA;
ulong_t 	fl;
ulong_t 	blkno;
{
	      VMOD	  od;
	      int	  rc;
	      ulong_t	  ipg;
	      ulong_t	  cpg;
    register  VMOD	  *pod = SSToDS(&od);
	      ulong_t	  dfl;
	      SEL	  sel;
	      ulong_t	  cbdesc;

    /*
     *	Get object info for containing object.	Note that this involves
     *	taking the handle semaphore and we use this to make us wait for
     *	short term locks to clear if decommitting.
     *	We don't need to wait if we're committing new pages
     *	since that operation does not alter the virtual-to-physical mapping of
     *	existing (possibly locked) pages.
     *
     *	B731252 - Don't wait for short locks to clear if changing access attr
     */

    pod->od_laddr = p;
    if(VMSysArena(p))
    {
	hobPTDA = HOBNULL;
    }
    pod->od_pPTDA = VMGetPTDAAddr(hobPTDA);

    if((rc = VMGetOd(VMGS_TAKESEM |
		     ((fl & VMS_DECOMMIT) ? VMGS_SLOCKWAIT : NA),
		     pod)) != NO_ERROR)
    {
	return(rc);
    }

    /*
     *	Get the per-context default object permissions.
     */

    dfl = VMGetObPermOd(pod);

    /*
     *	If default permissions were requested, don't allow specific
     *	permissions as well.
     */

    if(fl & VMS_DFLTPERM)
    {
	vmAssert((fl & (PG_R | PG_W | PG_X | PG_GUARD)) == 0);
	fl = (fl & ~VMS_DFLTPERM) | dfl;
    }

    /*
     *	If this is a loader object and permissions are being set, make
     *	sure that they don't exceed the initial allocation permissions.
     */
//  PMWIN Fix: they need the ability to put a loader r/w object into the GSR
//
//  if(vmIsLoadOb(pod->od_pob) && !(fl & VMS_DECOMMIT) &&
//     ((fl & PG_PERM-PG_GUARD) & ~dfl))
//  {
//	VMClearSem(vmPobToHob(pod->od_pob));
//	return(ERROR_ACCESS_DENIED);
//  }
//  end PMWIN

    /*
     *	Set new object permissions if VMS_INCPERM was specified.
     */

    if(fl & VMS_INCPERM)
    {
	fl |= dfl;
	if ((fl & PG_PERM) & ~dfl)
	{
	    vmSetObPermOd(pod, fl & PG_PERM);
	}
    }

#     if (VMS_INCPERM != PG_INCPERM)
#	  error "flags conflict"
#     endif

    if(fl & (VMS_COMMIT | VMS_DECOMMIT))
    {
	/*
	 *  If committing or decommitting memory, call VMSetMemSub.
	 */

	/*  Round up the size to cover to the nearest page size if the object
	 *  is not a base selector map all object. The
	 *  address will be rounded up or down as necessary by VMSetMemSub.
	 */

	if (!VMBaseSelMapAllOd((*pod)))
	{
	    cb += PAGEMASK - ((p + cb - 1) & PAGEMASK);
	}

	sel = (SEL) (((pod->od_par)->ar_f & AR_SELMAP) ? VMSelOd(*pod) : NA);

	if (sel != NULL)
	{
	    rc = SELGetSize(pod->od_laddr, sel, hobPTDA, VMSizeOd(*pod),
			    SSToDS(&cbdesc));

	    vmAssert(rc == NO_ERROR);

	    /*
	     *	For commit requests, request selector update if the
	     *	descriptor's limit doesn't already cover the committed
	     *	region. For base selector map all requests, always update
	     *	the descriptor limit.
	     */


	    if (((fl & VMS_COMMIT) && ((pod->od_laddr + cbdesc) < (p + cb))) ||
		VMBaseSelMapAllOd(*pod))
	    {
		fl |= VMS_SELMAP;
	    };

	}
	else
	{
	    cbdesc = NULL;
	}

	rc = VMSetMemSub(fl, pod, p, cb, blkno, cbdesc);

    }
    else
    {
	/*
	 *  Perform the set operation.
	 */

	cpg = VMRegionSize(p, cb) >> PAGESHIFT;
	ipg = VMRegionBase(p) >> PAGESHIFT;

	rc = PGSetAccess(ipg, pod->od_pPTDA, cpg, fl);

	/*
	 *  Set up descriptors' permissions. VMSetMem only increases
	 *  descriptor permissions, it never decreases them.
	 */

	if (rc == NO_ERROR)
	{
	    SELAddPermission(ipg, pod->od_pPTDA, cpg,
				 fl & (PG_R | PG_X | PG_W));
	}
    }

    VMClearSem(vmPobToHob(pod->od_pob));
    return(rc);
}

#pragma code_seg()
#pragma code_seg("SWAP32_3","CODE")
/***EP	VMSetMemSub - Commit/decommit memory
 *
 *	VMSetMemSub comits or decommits memory, and updates the
 *	descriptors if necessary on behalf of VMSetMem and selReallocSub.
 *	Object semaphore must be taken, and object information buffer
 *	must be filled. Aliases can not be committed or decommitted.
 *	The range to be committed or decommitted can not cross object
 *	boundary.
 *	Descriptors are updated only if VMS_SELMAP flag is passed.
 *	WARNING: This procedure is in swappable code
 *
 *	int	VMSetMemSub(ulong_t fl, VMOD *pod, ulong_t p, ulong_t cb,
 *			ulong_t blkno, ulong_t cbdesc)
 *
 *	ENTRY	    fl	- Object permissions, commit/decommit flag
 *			    VMS_COMMIT, VMS_DECOMMIT, PG_EXECUTE, PG_READ,
 *			    PG_WRITE, PG_GUARD, VMS_SELMAP, PG_SKIPFREE,
 *			    PAS_*, PG_*, PG_SHARED
 *			    PG_*, PAS_* and PG_* flags needed only when
 *			     VMS_COMMIT
 *		    pod - Pointer to object descriptor buffer
 *		    p	- Starting address of the region
 *		    cb	- size of the region to be committed.
 *		  blkno - Loader block no.
 *			    (Needed only for loader originated objects)
 *		 cbdesc - Current descriptor's limit (if any)
 *	RETURN	    int - return code (NO_ERROR if successful)
 *
 *	PseudoCode:
 *
 *	If (decommit request && object locked in current context)
 *	    return error
 *	If (range belongs to an alias, or crosses object boundary and not
 *	  a VDM context)
 *	    return error
 *	If (selector update requested)
 *	    Find SELSetDescInfo flags (SELSD_SHARED, SELSD_ALIAS)
 *	If (commit request)
 *	    PGAlloc	    ; allocate memory
 *	    If (shared or aliased)
 *		VMEachMap(PGAttach+SELAlloc+SELSetDescInfo)
 *	    Else
 *	      If (selector update requested)
 *		SELAllocLDT/GDT
 *		SELSetDescInfo
 *	      Endif
 *	    Endif
 *	Else		    ; decommit
 *	    If (shared or aliased)
 *		VMEachMap(PGFree, if sel requested SELSetDescInfo)
 *	    Else
 *		PGFree
 *		If (selector update requested)
 *		    SELSetDescInfo
 *		Endif
 *	    Endif
 *	Endif
 *	return
 */
int	    VMSetMemSub(fl, pod, p, cb, blkno, cbdesc)
ulong_t     fl; 		/* commit/decommit, permission, sel update */
register    VMOD *pod;		/* Object info buffer */
ulong_t     p;			/* Starting address of the region */
ulong_t     cb; 		/* size of region */
ulong_t     blkno;		/* Loader blockno */
ulong_t     cbdesc;		/* Current descriptor's limit */
{
	     int      rc = NO_ERROR;
	     VMSD     sd;
	     ulong_t  ipg;
	     ulong_t  cpg;
	     SEL      sel;
	     struct   seldesc_s desc;
	     ulong_t  flsel;
    register VMSD     *psd = SSToDS(&sd);
	     VMHOB    hobPTDA = VMHobPTDAOd(*pod);

    /*
     *	Make sure PG and VM flags do not overlap
     */

    vmAssert(((VMS_COMMIT | VMS_DECOMMIT | VMS_SELMAP |
	       VMS_DFLTPERM | VMS_INCPERM | VMAC_SHARED) &
	       (PG_R |	PG_X | PG_W | PG_GUARD | PG_SKIPFREE)) == NULL);

    vmAssert((fl & VMS_COMMIT) ^ (fl & VMS_DECOMMIT));

    /*
     *	Make sure we own object semaphore.
     */

    vmSemAssertOb(pod->od_pob);

    /*
     *	If we are decommitting, there must not be any locks.
     */

    vmAssert(!(fl & VMS_DECOMMIT) || ((pod->od_pob)->ob_cslock == 0));

/* BUGBUG - IDB 03/29/90 - Allow decommits if no conflict with actual
** pages involved>  Temporarily fix this by commenting out llock check
**
**  if((fl & VMS_DECOMMIT) && ((pod->od_pob)->ob_cllock > 0))
**  {
**	return(ERROR_LONGLOCK);
**  }
**/
    /*
     *	Make sure it is not an alias
     */

    if(VMIsAliasOd((*pod)))
    {
	return(ERROR_ALIAS);
    }

    /*
     *	Check that range lies within object.
     */

    if(!vmRangeWithinAr(p, cb, pod->od_par))
    {
	return(ERROR_CROSSES_OBJECT_BOUNDARY);
    }

    /*	Now convert p and cb to ipg and cpg. Here's how we do it if they
     *	are not page granular:
     *	In a commit request; we always include the page p is in if it is
     *	not committed. If committed, we exclude the page if
     *	(poi->oi_va+cbdesc) <= p, and return ERROR_ACCESS_DENIED otherwise.
     *	In a decommit request; we exclude the page p is in if VMS_SELMAP
     *	is requested (call originated from SEL Manager), include it
     *	if not (call originated from VM).
     *	In each case we adjust cpg by the change in p, and round up
     *	to nearest page size.
     */

    if((fl & (VMS_DECOMMIT | VMS_SELMAP)) == (VMS_SELMAP | VMS_DECOMMIT))
    {
	ipg = (p + PAGEMASK) >> PAGESHIFT;
	cpg = (cb + p - (ipg << PAGESHIFT) + PAGEMASK) >> PAGESHIFT;
    }
    else
    {
	cpg = VMRegionSize(p, cb) >> PAGESHIFT;
	ipg = VMRegionBase(p) >> PAGESHIFT;
    }

    /*
     *	Set up VMSD fields for VMEachMap worker
     */

    psd->sd_fl = fl;
    psd->sd_blkno = blkno;
    psd->sd_ipg = ipg;
    psd->sd_cbdesc = 0;
    psd->sd_pPTDA = pod->od_pPTDA;
    psd->sd_sel = 0;
    psd->sd_csel = 0;

    if(fl & VMS_SELMAP)
    {
	if(cbdesc == NULL)
	{
	    /*
	     *	Completely sparse object, selectors not yet allocated
	     */

	    psd->sd_csel = -1;
	    psd->sd_cbdesc = p + cb - pod->od_laddr;
	}
	else
	{
	    if(VMTiledPg(ipg))
	    {
		/*
		 * Calculate # of selectors needed to allocate
		 */
		psd->sd_sel = LaToSelTiled(pod->od_laddr + cbdesc +
					   SEL_64K - 1);

		psd->sd_csel = (((ipg + cpg) << PAGESHIFT) -
				SelToLaTiled(psd->sd_sel)  +
				SEL_64K - 1) >> WORDSHIFT;
	    }
	    /*
	     *	Set up SELSetDescInfo Flags
	     */

	    flsel = SELSD_LIMIT;

	    if(((pod->od_pob)->ob_fs & OB_SHARED) &&
	       (VMSearchRef(VMSR_TWO, vmPobToHob(pod->od_pob)) == NO_ERROR))

	    {
		flsel |= SELSD_SHARED;
	    }

	    if((pod->od_pob)->ob_fs & OB_LALIAS)
	    {
		flsel |= SELSD_ALIAS;
	    }

	    if(VMBaseSelMapAllOd(*pod))
	    {
		flsel |= SELSD_BASEALL;
	    }

	    /*
	     * We also need the descriptor's access byte.
	     */

	    sel = VMSelOd(*pod);
	    rc = SELGetDescInfo(sel, hobPTDA, SSToDS(&desc));
	    vmAssert(rc == NO_ERROR);
	}
    }

    /*
     *	Now commit or decommit memory
     */

    if((fl & VMS_COMMIT) && (cpg != NULL))
    {
	if((rc = PGAlloc(ipg, pod->od_pPTDA, cpg, blkno,
			  vmPobToHob(pod->od_pob),
			  ipg - (pod->od_laddr >> PAGESHIFT),
			  fl & VMAC_PAFLAGS)) == ERROR_ACCESS_DENIED)
	{
	    /*
	     *	If the commit request was past the current selector limit,
	     *	but within the same page as the limit, AND that page is
	     *	committed (in which case we just got an error back from
	     *	PGAlloc), try to commit starting from the next page
	     */

	    if(p >= pod->od_laddr + cbdesc)
	    {
		rc = NO_ERROR;

		/*
		 *  If the caller only wanted to commit a single page
		 *  (and we know that it is committed already), just go
		 *  on as if our job is done.
		 */

		if(--cpg != 0)
		{
		    ipg++;
		    psd->sd_ipg = ipg;
		    rc = PGAlloc(ipg, pod->od_pPTDA, cpg, blkno,
				   vmPobToHob(pod->od_pob),
				   ipg - (pod->od_laddr >> PAGESHIFT),
				   fl & VMAC_PAFLAGS);
		}
	    }
	}
	if(rc == NO_ERROR && cpg != 0)
	{
	    if (((pod->od_pob)->ob_fs & OB_SHARED) || VMHasAliasOd(*pod))
	    {
		rc = VMEachMap(vmCommitMemWrkr, psd, ipg, hobPTDA, cpg,
			       VMEM_ERRSTOP | VMEM_ALL,
			       vmDecommitMemWrkr, psd);
	    }
	    else
	    {
		/*
		 * Single context, avoid VMEachMap overhead by calling worker
		 * directly.
		 */
		rc = vmCommitMemWrkr(psd, ipg, cpg, pod, NULL);
	    }

	    if(rc != NO_ERROR)
	    {
		/*
		 *  Free the pages that were allocated by PGAlloc above.
		 *  There is a chance they were already freed by the
		 *  VMEachMap decommit worker, so use the skipfree flag.
		 */

		PGFree(ipg, pod->od_pPTDA, cpg, PG_SKIPFREE);
	    }
	}				    /* End PGAlloc succeeded block */
    }					    /* End commit block */

    if((fl & VMS_DECOMMIT) && (cpg != NULL))
    {
	/* Free request */

	if(((pod->od_pob)->ob_fs & OB_SHARED) || VMHasAliasOd(*pod))
	{
	    rc = VMEachMap(vmDecommitMemWrkr, psd, ipg, hobPTDA, cpg,
			   VMEM_ERRSTOP | VMEM_ALL,
			   (int *) NULL, (void *) NULL);
	}
	else
	{
	    /*
	     * Single context, avoid VMEachMap overhead by calling worker
	     * directly.
	     */
	    rc = vmDecommitMemWrkr(psd, ipg, cpg, pod, NULL);
	}
    }

    /*
     *	Update the descriptors if needed.
     */

    if((rc == NO_ERROR) && (fl & VMS_SELMAP) && (cbdesc != NULL))
    {
	SELSetDescInfo(flsel,
		   (ushort_t)((desc.desc_attr << BYTESHIFT) | desc.desc_acc),
		   sel,
		   (fl & VMS_COMMIT) ? (cb + p - pod->od_laddr) :
				       (p - pod->od_laddr),
		   pod->od_laddr, vmPobToHob(pod->od_pob), hobPTDA, cbdesc);
    }

    return(rc);
}


#pragma code_seg()
#pragma code_seg("SWAP32_4","CODE")

/***LP	vmCommitMemWrkr - VMEachMap worker function for committing memory.
 *
 *	This function is called by VMEachMap on behalf of VMSetMemSub for
 *	every context in which a specified range of pages is partly or
 *	wholly mapped.	The function calls the Page Manager to perform
 *	the appropriate action (commit or decommit pages), and the
 *	selector manager to allocate/setup descriptors if necessary.
 *	WARNING: This procedure is in swappable code
 *
 *	int vmCommitMemWrkr( *psd, ipg, cpg, pod, pgoff)
 *
 *	ENTRY	psd		- pointer to structure containing flags, blkno
 *		ipg		- start page address of the map
 *		cpg		- size of the map
 *		pod		- pointer to object descriptor structure
 *		pgoff		- offset of the start of the map from the
 *				  passed address
 *
 *	RETURN	rc		- result code
 *				    NO_ERROR
 *				      success
 *				    Errors from Page Manager
 */

int vmCommitMemWrkr(psd, ipg, cpg, pod, pgoff)
register    VMSD *psd;
ulong_t     ipg;
ulong_t     cpg;
register    VMOD	*pod;
ulong_t     pgoff;
{
	     int     rc = NO_ERROR;
	     int     fl;
	     ulong_t flsel;
	     SEL     sel;

    fl = VMGetObPermOd(pod);

    /*
     *	Commit the memory by calling PGAttach if this isn't the requested
     *	context.
     */

    if(pod->od_pPTDA != psd->sd_pPTDA || pgoff != 0 || VMIsAliasOd((*pod)))
    {
	rc = PGAttach(ipg, pod->od_pPTDA, psd->sd_ipg + pgoff,
			  psd->sd_pPTDA, cpg, fl);
    }

    /*
     *	Allocate selectors if needed. We only need this if we are not
     *	iterating over an alias, and csel != 0, or the object/alias is a
     *	windows 3.0 style 'basemapall' object.
     *	If csel == -1, selectors have never been set up yet. Then we have
     *	to call SELAlloc.
     */

    if((rc == NO_ERROR) && (psd->sd_fl & VMS_SELMAP) &&
	(psd->sd_csel != NULL) &&
	(VMBaseSelMapAllOd((*pod)) || !VMIsAliasOd((*pod))))
    {
	if (psd->sd_csel == -1)
	{
	    /*
	     *	We need to allocate all descriptors from the beginning
	     *	of the object to the end of the last committed region.
	     */

	    /*
	     *	Construct SELAlloc flags. We assume we always have to
	     *	update data selectors
	     */

	    flsel = SELAL_SELMAP | ((fl & PG_W) ? SELAL_W : 0) |
			      ((fl & PG_U) ? (SEL_RPL3 << SELAL_PRIVSHIFT) :
					     (SEL_RPL2 << SELAL_PRIVSHIFT));

	    if(VMBaseSelMapAllOd((*pod)))
	    {
		flsel |= SELAL_BASEALL;
	    }

	    /*
	     *	Add a few more flags needed by SELAlloc
	     */

	    fl |= (VMSysArena(pod->od_laddr) ? VMAC_ARENASYS :
		  (VMShrArena(pod->od_laddr) ? VMAC_ARENASHR :
					       VMAC_ARENAPRV));

	    rc = SELAlloc(pod->od_pob, pod->od_par, psd->sd_cbdesc, fl,
			      flsel, pod->od_pPTDA, SSToDS(&sel));

	    if((rc == NO_ERROR) && !(sel & SEL_LDTBIT))
	    {
		(pod->od_par)->ar_sel = sel;
	    }
	}
	else
	{
	    vmAssert(VMTiled(pod->od_laddr) && (psd->sd_sel != NULL));

	    rc = SELAllocLDT(NULL, psd->sd_sel, (ushort_t) (psd->sd_csel),
			     VMHobPTDAOd(*pod));
	}

	if(rc != NO_ERROR)
	{
	    /*
	     *	SELAlloc or SELAllocLDT failed. We have to undo PGAttach
	     */
// GSR start
	    if (pod->od_laddr < MAXGLOBALSHRLADDR && pod->od_laddr >= MINGLOBALSHRLADDR)
	    {
	       return(rc);
	    }
// GSR end

	    PGFree(ipg, pod->od_pPTDA, cpg, 0);
	}
    }

    return(rc);
}

#pragma code_seg()
#pragma code_seg("SWAP32_7","CODE")

/***LP	vmDecommitMemWrkr - VMEachMap worker error function for VMSetMem
 *
 *	This function is called by VMEachMap on behalf of VMSetMemSub when
 *	decommitting memory, or when an error has occurred during commit
 *	operation.
 *	The function calls the Page Manager to decommit pages.
 *
 *	int vmDecommitMemWrkr( psd, ipg, cpg, pod, pgoff)
 *
 *	ENTRY	psd		- address of data area passed to this
 *				  function.  Data area contains flags and
 *				  block number supplied on VMSetMem request.
 *		ipg		- start virtual page number for region
 *		cpg		- size of region in virtual pages
 *		pod		- pointer to object descriptor structure
 *		pgoff		- offset of the start of region from the
 *				  passed address
 *
 *	RETURN	rc		- result code
 *				    NO_ERROR
 *				      success
 *				    Errors from Page Manager
 */

int	    vmDecommitMemWrkr(psd, ipg, cpg, pod, pgoff)
VMSD	    *psd;
ulong_t     ipg;
ulong_t     cpg;
register    VMOD	*pod;
ulong_t     pgoff;
{
	     int     rc;
// GSR start
    VMCO		*pco;
    VMHCO		hco;
// GSR end

    /*
     *	If we came here as a result of error condition, we may have to
     *	unset the descriptors set by SELAlloc
     */

    if (psd->sd_csel == -1)
    {
	SELFree(pod);

	if (VMSysArena(pod->od_laddr))
	{
	    (pod->od_par)->ar_sel = 0;
	};
    };

    /*
     *	Calls originated from selRealloc may have invalid regions
     */
// GSR start

       // if we're in the global shared region only call PGFree for
       //   the last process on the list since we only have to call
       //   pgfree once

// KDH - Correct the checking of AR_HCO flag (Defect 156118).
       if (((pod->od_par)->ar_f & AR_HCO)  &&
	    pod->od_laddr < MAXGLOBALSHRLADDR && pod->od_laddr >= MINGLOBALSHRLADDR)
       {
	   pco = VMPcoOd(*pod);
	   if (pco->co_hconext != HCONULL) {
	       return(0);
	   }
       }
// GSR end

    rc = PGFree(ipg, pod->od_pPTDA, cpg,
		VMBaseSelMapAllOd((*pod)) ? PG_SKIPFREE :
					    psd->sd_fl & PG_SKIPFREE);

    /* error recovery case must not fail */
    vmAssert(!(psd->sd_fl & VMS_COMMIT) || (rc == NO_ERROR));

    return(rc);
}

#pragma code_seg()
#pragma code_seg("SWAP32_27","CODE")

/***EP	VMFreePseudoHandle - Free a pseudo-object
 *
 *	This function frees a the object record for a pseudo-object
 *	WARNING: This function is in swappable code.
 *
 *	VMFreePseudoHandle (p, hob)
 *
 *	ENTRY	hob = pseudo-handle created by VMCreatePseudoHandle
 *	RETURN	none
 *
 *	Check that the handle maps a pseudo object.
 */

void VMFreePseudoHandle(hob)
VMHOB		hob;		/* pseudo-handle */
{
    VMOB *pob;			/* pointer to object record */
    pob = vmHandleMap(hob);	/* Get pointer to object record */
    vmAssert(pob->ob_fs & OB_PSEUDO);
				/* Must be a pseudo object with matching
				   address */
    vmAssert(pob->ob_wsemowner == NULL);
				/* Must not have a semaphore taken */
    vmObFree(pob);		/* Free the object record */
}

#pragma code_seg()
#pragma code_seg("VMSWAP_TEXT","CODE")

/***EP	VMFreeMemOb - Free memory object from context given handle.
 *
 *	This function frees a memory object given a handle to it and a
 *	context.  It is provided as an optimization of VMFreeMem for special
 *	callers such as the loader.  The handle is validated and the
 *	corresponding virtual address is obtained from the base arena record.
 *	VMFreeMem is then called with a special flag indicating that the
 *	bitmap arena record location code may be bypassed on the first loop.
 *
 *	WARNING: This function is in swappable code
 *
 *	ENTRY	hob		- handle to object
 *		hobPTDA 	- PTDA handle
 *		fl		- flags
 *				    VMFM_TASK_TERM
 *				      Task termination
 *				    VMFM_VDM
 *				      Request on behalf of a VDM
 *	RETURN	int		- return code
 *				    NO_ERROR
 *				      Free was successful
 *				    ERROR_LONGLOCK
 *				      Long term locks outstanding
 *				    ERROR_INVALID_ADDRESS_RANGE
 *				      Address range does not exactly
 *				      match that of an object after
 *				      rounding to page granularity
 */

int			VMFreeMemOb(hob,hobPTDA,fl)
VMHOB			hob;		/* Handle */
VMHOB			hobPTDA;	/* PTDA handle */
ulong_t 		fl;		/* Flags */
{
  VMOD			od;		/* Object info buffer */

  /*
   *  Validate flags, checking that only defined flags have been specified.
   */

  vmAssert((fl & ~(VMFM_GC | VMFM_TASK_TERM | VMFM_VDM)) == NA);

  /*
   *  Obtain the address from the object handle by mapping to the arena record.
   */

  od.od_pPTDA = VMGetPTDAAddr(hobPTDA);
  od.od_pob = VMHandleMap(hob);
  return(VMFreeMemOd(fl | VMFM_HOB, SSToDS(&od)));
}

#pragma code_seg()
#pragma code_seg("SWAP32_4","CODE")

/***EP	VMFreeMem - Free reference to an object given base address.
 *
 *	This function frees a reference to an object from a given context
 *	given the base virtual address of the object.  The function can also
 *	be used to free an alias to a subregion of the object, or a region
 *	of virtual address space (reserved by a single arena record) that does
 *	not correspond to an object (for example, a 64K region in the tiled
 *	area corresponding to a call-gate selector or a region retained after
 *	an MTE object is freed.
 *
 *	If there are memory resources committed to the region, they are freed
 *	if the address range is in the system arena, or if there are no
 *	references from other contexts and there are no outstanding CS aliases
 *	or resource segment references.  If the address space is in the system
 *	arena, in the shared arena and not referenced by other contexts, or in
 *	the private arena, it is dereserved.  Except for VDM's, the region of
 *	memory must (after rounding to page granularity or, if tiled, 64K
 *	granularity) map to an entire virtual memory object as created by a
 *	preceding VMAllocMem call.  VDM's may free any region in their own
 *	private arena.
 *
 *	To decommit memory resources without dereserving address space a task
 *	must use the VMSetMem function.  VMFreeMem does not performs optional
 *	dereserves.
 *
 *	An alias to a subregion of a memory object may also be specified.  In
 *	this case the alias is simply freed; no decommit of memory resources
 *	takes place.
 *
 *	If the addressed memory has aliases and the specified address range is
 *	not one of them, VMFreeMem acts as follows.  While non-debug aliases
 *	still exist, VMFreeMem notifies the debugging task for each one in
 *	turn, and waits for the debugger to run.  The debugger normally frees
 *	the alias by calling VMFreeMem.  Once all debug aliases have been
 *	freed, VMFreeMem then frees all non-debug aliases.
 *
 *	The action of VMFreeMem depends on the type of arena in which the
 *	virtual address space is located, whether the memory is shared or
 *	private, and whether the virtual address space is an alias.  The
 *	various cases are as follows.
 *
 *	Case I	- The address range is an alias
 *
 *	In this case, VMFreeMem simply dereserves the addresses.
 *
 *	Case II - Non-alias system arena
 *
 *	VMFreeMem decommits any memory assigned to the addresses and
 *	dereserves the addresses.  They become unaddressible from all contexts.
 *
 *	Case III - Shared arena, shared data
 *
 *	VMFreeMem makes the addresses invalid in the current context only.
 *	If the last reference to the shared data is going away, VMFreeMem
 *	decommits any committed memory assigned to the object having the
 *	specified base address and in the given context and invalidates
 *	the addresses in all contexts.	Note that shared arena addresses may
 *	not be aliases.
 *
 *	Case IV - Shared arena, private data
 *
 *	VMFreeMem decommits any committed memory assigned to the object
 *	with the specified base address and in the given context.  The
 *	addresses are invalidated in the given context.
 *
 *	Case V - Private arena, shared data
 *
 *	VMFreeMem makes the addresses invalid in the current context.
 *	If the last reference to the shared data is going away, VMFreeMem
 *	decommits any committed memory assigned to the address range.
 *
 *	Case VI - Private arena, private data
 *
 *	VMFreeMem decommits any committed memory assigned to the object
 *	with the specified base address and in the given context.
 *	and context.  The addresses are invalidated in the given context.
 *
 *	WARNING: This function is in swappable code
 *
 *	VMFreeMem (p, hobPTDA, fl)
 *
 *	ENTRY	p			- region's virtual address
 *		hobPTDA 		- PTDA handle
 *		fl			- Flags
 *					  VMFM_TASK_TERM - call is from task
 *					    termination
 *					  VMFM_VDM - call on behalf of VDM
 *					  VMFM_GC - call from Garbage Collection
 *	RETURN	int			- Result code
 *					    NO_ERROR = success
 *					    non-zero = failure
 *					    NOTE: ERROR_INTERRUPT will not
 *					    be returned.  All free operations
 *					    ignore interrupts.
 *
 *	Validate the address and context.  Take ownership of the object's
 *	semaphore.  If not a request to free an alias, check for debug aliases
 *	that need to be removed and loop to perform debugger notifications;
 *	then. remove non-debug aliases.  Free the memory commitment (if system
 *	arena, private data, or shared data in which the last reference is
 *	going away.  Free the selectors, if any.  Remove the object from the
 *	given context; if the addresses are no longer in use, then free the
 *	virtual address space; if the last reference to the object is removed,
 *	then mark the object record as dead.  Release the object's semaphore.
 *	If the object is dead, then free the object record.
 *
 *	CALLS	vmAssert, VMRegionBase, VMRegionSize, vmFreeMemNotify,
 *		vmFreeMemContext, vmObFree, VMClearSem, vmArMap,
 *		VMSysArenaPg, PGFree.
 *
 *		This call will return ERROR_LONGLOCK if there are
 *		long-term locks outstanding.
 *
 *	EFFECTS Changes data structure linkages.  See callees' effects.
 *
 *	PSEUDO CODE
 */

int			VMFreeMem(laddr, hobPTDA, fl)
ulong_t 		laddr;		/* Linear address */
VMHOB			hobPTDA;	/* PTDA handle */
ulong_t 		fl;		/* Flags */
{
  VMOD			od;		/* Object data Structure */
  VMKHIB		khib;		/* Heap info buffer */
  SEL			sel;		/* Selector if heap */
  int			rc;		/* return code */

  MMPHPreVMFreeMem();			// Performance Hook

  /*
   *  Validate flags, checking that only allowed flags have been specified.
   */

  vmAssert((fl & ~(VMFM_GC | VMFM_TASK_TERM | VMFM_VDM | VMFM_NOLDR |
		  VMFM_ALLOW_HEAP | vmfm_SHR_SEM)) == NA);

  /*
   * See if it is a heap free. If so find out which heap, and call
   * VMFreeKHB
   */

  if (fl & VMFM_ALLOW_HEAP)
    {
      rc = VMGetInfoKHB((char *) laddr, SSToDS(&khib));
      if (rc == NO_ERROR)
	{
	  /*
	   * It is a heap object. Validate the address and determine whether
	   * to call SELFreeKHB or VMFreeKHB
	   */
	  if ((rc = SELValidateKHBAddress((char *) laddr, SSToDS(&sel),
	    (VMHOB *)VM_NO_ADDR)) == NO_ERROR)
	    {
	      if (!sel)
		{
		  VMFreeKHB(khib.khib_hkh, (char *) laddr);
		}
	      else
		{
		  SELFreeKHB(khib.khib_hkh, NA, sel);
		}
	    }
	  MMPHPostVMFreeMem();		// Performance Hook
	  return(rc);
	}
      else
	{
	 /*
	  * Not a heap object
	  */
	 fl &= ~VMFM_ALLOW_HEAP;
	}
    }

  /*
   *  Store the base address and context in the object info buffer and
   *  call common code to do the free.
   */

  VMLaddrOd(od) = laddr;
  VMPTDAOd(od) = VMGetPTDAAddr(hobPTDA);
  rc = VMFreeMemOd(fl, SSToDS(&od));
  MMPHPostVMFreeMem();			// Performance Hook
  return(rc);
}
#pragma code_seg()
