#define SCCSID  "@(#)vmarena.c  6.12 92/03/18"

/*
 *       IBM/Microsoft Confidential
 *
 *       Copyright (c) IBM Corporation 1987, 1989
 *       Copyright (c) Microsoft Corporation 1987, 1989
 *
 *       All Rights Reserved
 *
 * 72417  12/22/95  MLM - Moved find.h after tkexport.h since most dirent
 *                        stuff is now in tkexport.h
 */

/*
 *      Arena location routines
 */

#define FNO_NUM  FNO_VMARENA_C
#include <fnomem.h>
#include <mmph.h>

/***LC  ARLOC - Arena Location Routines
 *
 *      The routines in this module enable a memory object mapping or
 *      linear address range to be found within an arena given usually a linear
 *      address within or above the range.  Pointers to the VM data structures
 *      related to the mapping are obtained and returned in a structure
 *      called the "VM object data", or just "object data" structure.
 *
 *      Exported functions
 *
 *      VMGetOd - Get object data and fill in VMOD structure.
 *      VMOdToOi - Fill in old VMOI structure from VMOD structure
 *      VMOiTOOd - Fill in VMOD structure from VMOI structure
 *
 *      Supporting functions
 *
 *      vmRecordOd - Record object data retrieval information for mapping
 *      vmEraseOd - Erase object data retrieval information for mapping
 *
 *      Design
 *
 *      Object data retrieval given a linear address and context is a
 *      three stage process using bitmaps, hash tables and arena, object
 *      and context record link traversals.  The basic idea is a four
 *      stage process.
 *
 *      Stage [1]  Find the arena corresponding to the object.  This is
 *                 either the system arena, shared arena or the private
 *                 arena for the given context.  An address range check
 *                 determines the arena; the arena headers for system and
 *                 shared are global data and the private arena header is
 *                 embedded in the resident portion of the specified PTDA.
 *
 *      Stage [2]  Obtain the base address of the relevant mapping.  This
 *                 is done by scanning the arena's bitmap.  Each bit in the
 *                 bitmap represents an arena-dependent fixed unit of linear
 *                 address space (4K or 64K bytes) known as the arena
 *                 granularity.  Base addresses of mappings may only have
 *                 arena granularity, and bits set in the bitmap are precisely
 *                 those that correspond to the those base addresses.
 *
 *                 NOTE:  This step is frequently ommitted as an optimization
 *                 for arenas with coarse (64K) granularity, (private non-
 *                 VDM and the shared arena are eligible for optimization).
 *
 *      Stage [3]  Obtain the arena record corresponding to the found
 *                 base address and arena.  This step uses a hash function
 *                 (dependent on arena type only) that operates on the
 *                 base address.
 *
 *      Stage [4]  Obtain the object record from the arena record.
 *
 *      Stage [5]  (Shared arena only).  Traverse the arena, object or
 *                 context record chains to uniquely determine the arena
 *                 objetc or context records for the given mapping.
 *
 *      Stage [6]  Fill in pointers to arena header, arena, object, context
 *                 records in the VMOD structure supplied by the client.
 *
 *      Path length Optimizations
 *
 *      (a)   Coarse (64K granular) arenas while no hash wrap
 *
 *      Shared and private (non-VDM) arenas are "tiled" and have 64K
 *      granularity.  Bitmap scanning can be bypassed in the common case
 *      where no hash table "wrapping" has occurred.  The hash function
 *      simply divides (shifts) the linear address by the arena granularity
 *      and "no hash wrapping" means that all reservations in the arena
 *      have "principal" base addresses, i.e. no lower address hashes to
 *      the same value (expand up private arenas) or no higher address
 *      hashes to the same value (expand down arenas).  A bit in the
 *      arena header is used to indicate that no hash wrapping has yet
 *      occurred.  If set, and the linear address falls between the
 *      lower and upper bounds of current reservations in the arena,
 *      the hash chain is of length 0 or 1.  If length 1, the arena
 *      record can be pulled straight out of the hash table.
 *
 *      (b)   Base address detection
 *
 *      If the given linear address rounded down to arena granularity has
 *      its bit set in the bitmap, bitmap scanning (Stage [2]) is bypassed.
 *
 *      (c)   Base address knowledge specified.
 *
 *      If the client knows that the given address is definitely a base
 *      address, the VMOD_BASE flag can be set when calling VMGetOd.  All
 *      bitmap scanning is bypassed.
 *
 *      Memory consumption optimizations
 *
 *      (d)   Sparse dynamically sized bitmaps and bitmap directory
 *
 *      The whole of an arena's bitmap is divided into smaller bitmaps
 *      indexed by a bitmap directory.  Bitmaps only exist where needed
 *      and are dynamically-sized kernel resident heap blocks.  Each bitmap
 *      can grow to an arena-dependent maximum size, representing a fixed
 *      maximum chunk of address space.  The bitmap directory is also
 *      dynamically grown as needed.
 *
 *      (e)  Dynamically sized hash table
 *
 *      The hash table is a dynamically sized kernel resident heap block
 *      and expands up to a maximum size as needed.
 *
 *      (f)  Two-part arena headers
 *
 *      Arena header info is divided into arena dependent and arena type
 *      dependent classes.  The former is contained in the main arena header
 *      (VMAH structure), one of which exists for every arena.  The latter
 *      is contained in a per-arena_type structure (VMAT structure), only
 *      one of which exists for each of the four arena types:
 *
 *      Type 0:  Private non-vdm tiled
 *      Type 1:  Private vdm
 *      Type 2:  Shared
 *      Type 3:  System
 *
 *      The VMAH structure contains a pointer to the appropriate VMAT
 *      structure; the four VMAT structures being declared as global data.
 *
 *      Data structures
 *
 *      Arena header (type VMAH)
 *
 *      The arena header contains all per-arena information, including
 *      a pointer to the sentinel arena record, flags, granularity information
 *      and pointers to the other object data location structures.
 *
 *      Arena per-type info (VMAT)
 *
 *      Bitmap directory (type VMBD)
 *
 *      The bitmap directory is an array of pointers to bitmaps (type VMBM).
 *      The address of the bitmap directory is stored in the arena header.
 *
 *      Bitmaps (type VMBM)
 *
 *      Each linear address "laddr" within an arena having a granularity "G"
 *      (typically 64K or 4K bytes, and always a power of two) that is exactly
 *      divisible by G is represented uniquely by a bit in some bitmap.  Linear
 *      addresses of this form are precisely those that can be the base address
 *      of some memory object or alias mapped by the arena.  When an object
 *      or alias mapping is created, the bit corresponding to its base address
 *      is set in the appropriate bitmap.  Bitmaps are implemented as variable
 *      sized arrays of "bitmap entries" (type VMBME), a bitmap entry being
 *      a doubleword (32 bits).  A bitmap can be grown up to an arena-dependent
 *      maximum size.  The range of G-granular linear addresses mapped by
 *      a given bitmap that is fully grown is defined by the bitmap number
 *      the arena granularity and bitmap capacity.  Bitmaps exist only where
 *      required; the bitmap directory has null pointers corresponding to
 *      unallocated regions of the arena that contain entire regions that
 *      would be mapped by one or more bitmaps.
 *
 *      Hash table (type VMHSH)
 *
 *      Each arena contains a variable sized hash table.  A hash table
 *      is an array of arena record handles, each handle marking the
 *      head of a hash chain.  The maximum size of a hash table is arena
 *      dependent.
 */

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

#define INCL_DOSP
#include <os2p.h>

#include <osmac.h>

#include <mi.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <pgexport.h>
#include <vmexport.h>
#include <vmowner.h>
#include <vmlocal.h>

#include <curdir.h>                     /* for tkptda.h */
#include <dirent.h>                     /* for tkptda.h */
#include <infoseg.h>                    /* for tkptda.h */
#include <screen.h>                     /* for tkptda.h */
#include <sem.h>                        /* for tkptda.h */
#include <sft.h>                        /* for tkptda.h */

#include <tkexport.h>
                                        /* 72417 moved find.h after tkexport.h */
#include <find.h>                       /* for tkptda.h */
#include <vmkh.h>            // must be after tkexport.h for KSEM definition
#include <tkptda.h>
#include <selxport.h>
#include <string.h>


/***LP  vmbmNumbFunc - Convert Linear Address to Bitmap Number
 *
 *      Given an arena's type info and a linear address, this function
 *      derives the bitmap number corresponding to the address.  The
 *      given linear address is assumed to be within the bounds of the
 *      arena.
 *
 *      ulong_t vmbmNumbFunc(VMAT *pat, ulong_t laddr)
 *
 *      ENTRY           pat             - Pointer to arena type info
 *                      laddr           - Linear address
 *      RETURN          ulong_t         - Bitmap number
 */

#define vmbmNumbFunc(pat, laddr) \
  ((laddr & pat->at_lbmNumbMask) >> pat->at_lbmNumbShift)


/***LP  vmbmeNumbFunc - Convert Linear Address to Bitmap Entry Number
 *
 *      Given an arena's per type info and a linear address, this
 *      function derives the bitmap entry number corresponding to the
 *      address.  The given linear address is assumed to be within the
 *      bounds of the arena.
 *
 *      ulong_t vmbmeNumbFunc(VMAT *pat, ulong_t laddr)
 *
 *      ENTRY           pat             - Pointer to arena header
 *                      laddr           - Linear address
 *      RETURN          ulong_t         - Bitmap entry number
 */

#define vmbmeNumbFunc(pat, laddr) \
  (((laddr) & (pat)->at_lbmeNumbMask) >> (pat)->at_lbmeNumbShift)


/***LP  vmbmeNumbMax - Get Maximum Bitmap Entry Number
 *
 *      Given the address of a bitmap, this function calculates
 *      the maximum bitmap entry number.  The bitmap and its pointer
 *      are assumed to be valid.
 *
 *      ulong_t vmbmeNumbMax(VMBM *pbm)
 *
 *      ENTRY           pbm             - Pointer to bitmap
 *      RETURN          ulong_t         - Maximum bitmap entry number
 */

#define vmbmeNumbMax(pbm) \
  ((vmSizeRegKRHBD((char *)(pbm)) - sizeof(VMBMH) - sizeof(VMBME)) / \
    sizeof(VMBME))


/***LP  vmbmeBitNumbFunc - Convert Linear Address to Bitmap Entry Bit Number
 *
 *      Given an arena's type info and a linear address, this
 *      function derives the bit number within the bitmap entry number
 *      corresponding to the address.
 *
 *      ulong_t vmbmeBitNumbFunc(VMAT *pat, ulong_t laddr)
 *
 *      ENTRY           pat             - Pointer to arena type info
 *                      laddr           - Linear address
 *      RETURN          ulong_t         - Bit number within bitmap entry
 */

#define vmbmeBitNumbFunc(pat, laddr) \
  (((laddr) & pat->at_lbmeBitNumbMask) >> pat->at_lbmeBitNumbShift)


/***LP  vmHashFunc - Hash function
 *
 *      Given an arena's type info and a linear address, this function
 *      derives a hash table index (hash number).
 *
 *      ulong_t vmHashFunc(VMAT *pat, ulong_t laddr)
 *
 *      ENTRY           pat             - Pointer to arena type info
 *                      laddr           - Linear address
 *      RETURN          ulong_t         - Hash Number
 */

#define vmHashFunc(pat, laddr) \
   (((laddr) & (pat)->at_lHashNumbMask) >> (pat)->at_lHashNumbShift)


/***LP  vmHashFuncTiled - Hash function for tiled arenas
 *
 *      Given a linear address, this function derives a hash table index (hash
 *      number).  The address is assumed to belong to the given tiled arena.
 *
 *      ulong_t vmHashFuncTiled(ulong_t laddr)
 *
 *      ENTRY           laddr           - Linear address
 *      RETURN          ulong_t         - Hash Number
 */

#define vmHashFuncTiled(laddr) (((laddr) >> 16) & 0x000001ffL)


/***LP  vmGetAH - Get arena header macro
 *
 *      This function obtains the arena header (if any) for the arena
 *      that contains the given linear page.
 *
 *      WARNING:    The given linear page is only range-checked against
 *                  the current arena bounds (minimum and maximum addresses
 *                  actually reserved) within the system, shared and current
 *                  private tiled arenas.  No error will be returned
 *                  for linear pages that fall within an arena's bounds
 *                  but do not belong to reserved address space within
 *                  the arena.
 *
 *                  This function is implemented as a macro
 *
 *      int vmGetAH(ulong_t lpg, VMAH *pah)
 *
 *      ENTRY           lpg             - Linear page
 *                      pah             - Pointer to arena header to
 *                                        be returned
 *      EXIT            pah             - If lpg within arena bounds
 *                                        pah = addr of arena header
 */

/*
#define vmGetAH(lpg, pah)                                               \
  (VMPrvTiledArenaPg(lpg, pPTDACur) ?                                   \
   (pah = &(pPTDACur->ptda_aahPrivate[0]), NO_ERROR) :                  \
   (VMShrArenaPg(lpg, pah) ?                                            \
    ((pah = &aahvmShared[0]), NO_ERROR) :                               \
    (VMSysArenaPg(lpg) ?                                                \
      ((pah = &ahvmSys), NO_ERROR) : vmError(ERROR_INVALID_ADDRESS))))
*/

#ifdef VMSTRICT

/***LP  vmErrorSub - Return an error
 *
 *      This function exists so that error branches of the VM code can
 *      be traced via the link address of the call to this function.
 *
 *      WARNING  This function must remain in resident code
 *
 *      ENTRY           rc              - Result code
 *      RETURN          rc              - Result code
 *
 */

int vmErrorSub(rc)
int                     rc;             /* Result code */
{
  return(rc);
}

#endif

/***EP  VMGetOd - Return Memory Object Virtual Memory Manager Data
 *
 *      Given a memory object's linear address, pointer to its object
 *      record or pointer to its arena record, and a pointer to a PTDA
 *      (where relevant, see warning), this function returns object
 *      information.  If requested, the object's semaphore will be taken.  For
 *      shared arena shared context objects, the PTDA semaphore can also be
 *      taken.
 *
 *      WARNING  This function must remain in resident code
 *      WARNING  If a non-null PTDA pointer is specified, it is the
 *               caller's responsibility to know that the PTDA will not
 *               be freed during any yield.
 *
 *      int VMGetOd(ulong_t fl, VMOD *pod)
 *
 *      ENTRY           fl              - Flags
 *                                          VMGS_* VMResGetSem flags
 *                                          VMOD_NEAREST_BELOW
 *                                            Find nearest reservation below.
 *                                            If not specified, search is made
 *                                            for a reservation that includes
 *                                            the given linear address.
 *                                            WARNING:  One of VMAC_ARENASYS
 *                                            /SHR/PRV must also be specified
 *                                          VMOD_CHECK_UVIRT
 *                                            Return error if uvirt map
 *                                          VMOD_ANY_CTXT
 *                                            Return info for any context
 *                                            (shared arena only)
 *                                          VMOD_PTDA_SEM
 *                                            Take the PTDA sem for target
 *                                            context
 *                                          VMAC_ARENASYS/SHR/PRV
 *                                            Specify arena type (only needed
 *                                            if VMOD_NEAREST_BELOW specified)
 *
 *      RETURN          int             - Result code (NO_ERROR if successful)
 *
 *      EFFECTS                         The object descriptor buffer is filled
 *                                      in with pointers to the arena record
 *                                      object record and context record
 *
 *      CALLS
 * -------------------------------------------------------------------------
 * 04/23/91 B722363 - If getting semaphore fails do not set flag indicating
 *                    semaphore was gotten.
 *
 */

int                     VMGetOd(fl, pod)
ulong_t                 fl;             /* flags */
register VMOD           *pod;           /* Return buffer */
{
  ulong_t               lbmNumb;        /* Bitmap number */
  ulong_t               lbmeNumb;       /* Bitmap entry number */
  ulong_t               lbmeBitNumb;    /* Bit Number within bitmap entry */
  ulong_t               lbmeBit;        /* Bitnumber within bitmap entry */
  register VMAH         *pah;           /* Arena header address */
  VMAT                  *pat;           /* Arena per-type data */
  VMAR                  *par;           /* Pointer to arena record */
  VMOB                  *pob;           /* Pointer to object record */
  VMCO                  *pco;           /* Pointer to context record */
  VMBM                  *pbm;           /* Bitmap pointer */
  VMBM                  **papbm;        /* Pointer into bitmap directory */
  VMHOB                 hobPTDA;        /* PTDA handle */
  VMHOB                 hob;            /* Object handle */
  VMHAR                 har;            /* Arena record handle */
  VMHCO                 hco;            /* Context record handle */
  int                   rc;             /* Result code */
  register ulong_t      laddr = pod->od_laddr;


  /*
   *  Verify that the arena type has been indicated if we are looking
   *  for a reserved linear address nearest below the given linear address.
   */

  vmAssert(fl == (fl & VMOD_MASK));
  vmAssert((!(fl & VMOD_NEAREST_BELOW)) ||
            vmSysArenaReq(fl) || vmShrArenaReq(fl) || vmPrvArenaReq(fl));

  /*
   *  Verify that any context has not been specified in conjunction
   *  with a private arena request.
   */

  vmAssert(!((fl & VMOD_ANY_CTXT) && vmPrvArenaReq(fl)));
  for(;;)
    {
      /*
       *  Use current PTDA if none specified.  Get the arena header and
       *  range-check the linear address, optimizing on the case where a
       *  private arena linear address within an existing object is given.
       */

      if(pod->od_pPTDA == NA)
        {
          pod->od_pPTDA = pPTDACur;
        }
      pah = &(pod->od_pPTDA->ptda_ah);

      /*
       *  Perform additional range checking only if the address is above
       *  the highest reservation in the private arena.
       */

      if(laddr >= pah->ah_laddrMax)
        {
          pah = &ahvmShr;
          if(laddr >= pah->ah_laddrMax)
            {
              pah = &ahvmSys;
              if(laddr < pah->ah_laddrMin)
                {
                  /*
                   *  Given linear address lies between highest reservation in
                   *  shared arena and lowest reservation in system arena.  If
                   *  we expected to find a reserved region, return an error.
                   *  If we are looking for the nearest object below and the
                   *  address is not below the absolute minimum for the system
                   *  arena, return the arena record for either the highest
                   *  reservation in the shared arena, or the sentinel for the
                   *  system arena.
                   */

                  if(fl & VMOD_NEAREST_BELOW)
                    {
                      if(vmShrArenaReq(fl))
                        {
                          pah = &ahvmShr;
                          if(laddr < pah->ah_pat->at_laddrAbsMax)
                            {
                              pod->od_par = vmArMap(pah->ah_parSen->ar_harprev);
                              pod->od_pah = pah;
                              return(NO_ERROR);
                            }
                        }
                      else
                        {
                          pah = &ahvmSys;
                          if(laddr >= pah->ah_pat->at_laddrAbsMin)
                            {
                              pod->od_par = pah->ah_parSen;
                              pod->od_pah = pah;
                              return(NO_ERROR);
                            }
                        }
                    }
                  rc = vmError(ERROR_INVALID_ADDRESS);
                  break;
                }
            }
          else if(laddr < pah->ah_laddrMin)
            {
              /*
               *  Given linear address lies outside all reservations and
               *  lies in between the highest reservation in the private
               *  arena and the lowest reservation in the shared arena.  If
               *  we expected to find a reserved region, return an error.  If
               *  we are looking for the nearest object below, the caller
               *  must have indicated whether the linear address is to be
               *  treated as belonging to a (to be extended) private or
               *  shared arena.  In the private arena case, the arena record
               *  for the highest reservation in the private arena will be
               *  returned; in the shared arena case, the boundary sentinel
               *  is returned.
               */

              goto inbetweenarenas;
            }
        }

      /*
       *  Verify that private arena sentinel exists.  Return error if not.
       *  BUGBUG IDB 9/25/90 - Convert to vmAssert when all loader
       *  violations have been caught.
       */

      else if(pah->ah_parSen == NULL)
        {
          return(ERROR_INVALID_ADDRESS);
        }

      else if(laddr < pah->ah_laddrMin)
        {
          /*
           *  Address lower than minimum in private arena.
           */

          goto inbetweenarenas;
        }

      /*
       *  Address lies between the lowest and highest reservation in an arena.
       *  If we're looking for an address within an existing instance of an
       *  object and the threshhold of hash records has not been exceeded
       *  go straight to the hash table.  Bitmap bypass is only done for
       *  tiled arenas, so we can use a faster hash function.
       */

      VMPahOd(*pod) = pah;
      if(((pah->ah_fl & VMAH_BITMAP_BYPASS) && !(fl & VMOD_NEAREST_BELOW)) ||
        (fl & VMOD_BASE))
        {
          /*
           *  Case [1] - Bitmap bypass possible and looking for enclosing
           *  reservation, or linear address is known to be a base address of
           *  a reservation.
           */

          har = *(pah->ah_paharHash + vmHashFuncTiled(laddr));
          while(har != HARNULL)
            {
              par = vmArMap(har);
              if(pah->ah_fl & VMAH_NO_HASH_WRAP)
                {
                  /*
                   *  Case [1A] - Case [1] plus no hash wrap.
                   *  This case is optimized as it is common for private
                   *  and shared tiled arenas.  No hash wrap means that
                   *  all hash chains are of length not exceeding one and
                   *  contain arena records for lowest addresses (grow up
                   *  arenas) or highest addresses (grow down arenas) that
                   *  hash to a given value.  Since the given linear address
                   *  has already been range-checked, the arena record for
                   *  the enclosing object is the one just pulled straight
                   *  from the hash table.
                   */

              findobj:

                  /*
                   *  Get the object and context records (if any). Assume
                   *  initially that the object record pointed to by this
                   *  arena record is the right one.  The only case in which
                   *  this is not true is shared arena private data (= DLL
                   *  instance data).  WARNING:  Entries are made here for
                   *  general case (VMOD_NEAREST_BELOW, hash wrap etc)
                   */

                  pod->od_par = par;
                  hob = par->ar_hob;

                  /*
                   *  If not shared arena, the object record (if any) is
                   *  unique.  If also there are no requirements to take
                   *  or wait on semaphores, we're done.
                   */

                  if(!VMShrArena(laddr))
                    {
                      if(hob != HOBNULL)
                        {
                          pod->od_pob = vmHandleMap(hob);
                          if(!(fl & VMGS_MASK))
                            {
                              pod->od_laddr = par->ar_ipg << PAGESHIFT;
                              return(NO_ERROR);
                            }
                          goto getsem;
                        }

                      /*
                       *  Objectless non-shared arena reservation.  Return
                       *  base address and special error.
                       */

                      pod->od_pob = NULL;
                      pod->od_laddr = par->ar_ipg << PAGESHIFT;
                      return(vmError(ERROR_NO_OBJECT));
                    }

                  /*
                   *  Shared arena.  If objectless reservation, continue
                   *  looking if we want to find the nearest reservation
                   *  below that is valid in a specific context.  Otherwise
                   *  return special error and the base address found.
                   */

                  if(hob == HOBNULL)
                    {
                      pod->od_pob = NULL;
                      pod->od_laddr = par->ar_ipg << PAGESHIFT;
                      if((fl & (VMOD_NEAREST_BELOW | VMOD_ANY_CTXT)) ==
                         VMOD_NEAREST_BELOW)
                        {
                          goto nearbelow;
                        }
                      return(vmError(ERROR_NO_OBJECT));
                    }

                  /*
                   *  Shared arena object.  If shared data, find the right
                   *  context record.
                   */

                  hobPTDA = pod->od_pPTDA->ptda_handle;
                  if(par->ar_f & AR_HCO)
                    {
                      vmAssert(hob != HOBNULL);
                      pod->od_pob = vmHandleMap(hob);
//                    hco = par->ar_hco;
                      hco = VMHcoAr(*par);
                      vmAssert(hco != HCONULL);
                      do
                        {
                          pco = vmCoMap(hco);
                          if(((fl & VMOD_ANY_CTXT) && !vmPrivCo(pco)) ||
                             ((!(fl & VMOD_ANY_CTXT)) &&
                                pco->co_hobptda == hobPTDA))
                            {
                              pod->od_pco = pco;
                              if(fl & VMOD_ANY_CTXT)
                                {
                                  pod->od_pPTDA =
                                    VMResGetPTDAAddr(pco->co_hobptda);
                                }
                              if((fl & (VMGS_MASK | VMOD_PTDA_SEM)) == NA)
                                {
                                  pod->od_laddr = par->ar_ipg << PAGESHIFT;
                                  return(NO_ERROR);
                                }

                              /*
                               *  Shared arena shared data. Semaphore
                               *  interaction wanted.
                               */

                              goto getsem;
                            }
                        }
                      while((hco = VMHconextCo(*pco)) != HCONULL);
//                    while((hco = pco->co_hconext) != HCONULL);
                      goto nearbelow;
                    }

                  /*
                   *  Shared arena object private data.  Need to find the right
                   *  object record.
                   */

                  do
                    {
                      pob = vmHandleMap(hob);
                      if((fl & VMOD_ANY_CTXT) || pob->ob_hobowner == hobPTDA)
                        {
                          pod->od_pob = pob;
                          if((fl & VMGS_MASK) == NA)
                            {
                              pod->od_laddr = par->ar_ipg << PAGESHIFT;
                              return(NO_ERROR);
                            }

                          /*
                           *  Shared arena instance data. Semaphore
                           *  interaction wanted.
                           */

                          goto getsem;
                        }
                    }
                  while((hob = pob->ob_hobnext) != HOBNULL);

                  /*
                   *  If nearest reservation below but specific context,
                   *  continue searching shared arena.
                   */

                nearbelow:

                  if((fl & (VMOD_NEAREST_BELOW | VMOD_ANY_CTXT)) ==
                     VMOD_NEAREST_BELOW)
                    {
                      har = par->ar_harprev;
                      par = vmArMap(har);
                      if(par != pah->ah_parSen)
                        {
                          goto findobj;
                        }

                      /*
                       *  Sentinel reached, fill in info and return.
                       */

                      pod->od_par = par;
                      pod->od_pob = NULL;
                      pod->od_laddr = par->ar_ipg << PAGESHIFT;
                      return(NO_ERROR);
                    }
                  return(vmError(ERROR_INVALID_ADDRESS));
                } /* end if hash wrap */

              else
                {
                  /*
                   *  Hash table wrap.  Need to verify that given linear
                   *  address lies within arena record.  If not, skip to
                   *  next hash link.
                   */

                  if(vmWithinAr(laddr, par))
                    {
                      goto findobj;
                    }

                  /*
                   *  Move to next hash chain link.
                   */

                  har = par->ar_harhash;
                }

            } /* end do while hashchain scan */

          /*
           *  Hash chain corresponding to given linear address did not
           *  yield an arena record that maps an enclosing region.  Need
           *  to scan the bitmap after all.  Drop thru.
           */
        }

      /*
       *  Case [2] - No bitmap bypass and/or VMOD_NEAREST_BELOW specified.
       *  Compute bitmap, bitmap entry and bit numbers for given address.
       *  Compute mask with ones set for all bits at or below given bit
       *  in bitmap entry.
       */

      pat = pah->ah_pat;
      lbmNumb = vmbmNumbFunc(pat, laddr);
      lbmeNumb = vmbmeNumbFunc(pat, laddr);
      lbmeBitNumb = vmbmeBitNumbFunc(pat, laddr);
      lbmeBit = 1 << lbmeBitNumb;
      papbm = pah->ah_papbm + lbmNumb;

      /*
       *  Since we have already verified that the linear address
       *  lies within the bounds of the lowest and highest reservations
       *  in the arena, it is an internal error for the bitmap number
       *  to exceed the highest slot number in the bitmap directory.
       */

      vmAssert(lbmNumb <= pah->ah_lbmNumbMax);

      /*
       *  Check whether the bitmap for the given linear address exists.
       *  If it does, test specially for the case where the linear address
       *  is the base of an object.  If not, call assembly function
       *  vmbmScan to find the base linear address of the highest
       *  reservation at or below the given address.
       */

      pbm = *papbm;
      if(pbm != NULL && lbmeNumb <= vmbmeNumbMax(pbm) &&
        (pbm->bm_abme[lbmeNumb] & lbmeBit))
        {
          fl |= VMOD_BASE;
        }
      else if(vmbmScan(pah->ah_papbm,   /* Start of bitmap directory */
              SSToDS(&lbmNumb),                 /* Start bitmap */
              SSToDS(&lbmeNumb),                /* Start bitmap entry */
              SSToDS(&lbmeBitNumb)))         /* Start bit in bitmap entry */
        {
          laddr = (lbmNumb << pat->at_lbmNumbShift) +
                  (lbmeNumb << pat->at_lbmeNumbShift) +
                  (lbmeBitNumb << pat->at_lbmeBitNumbShift);
          pat = pah->ah_pat;
        }
      else
        {
     inbetweenarenas:

          /*
           *  Come here if in between private/shared or shared/system
           *  arenas, or below private arena.  Always return error if not a
           *  "nearest below" request.  For nearest below requests, return no
           *  error and sentinel of upper arena if that arena's absolute
           *  minimum address is not above the given address.  Note
           *  that the shared arena comparison is with its theoretical
           *  absolute minimum which may not be attainable because
           *  some private arena is in the way.
           */

//        if(fl & VMOD_NEAREST_BELOW)    replaced by next line ptr b7??????
          if((fl & VMOD_NEAREST_BELOW) && (!(fl & VMGS_TAKESEM)) )
            {
              if((vmShrArenaReq(fl) || vmSysArenaReq(fl)) &&
                  laddr >= pah->ah_pat->at_laddrAbsMin)
                {
                  pod->od_pah = pah;
                  pod->od_par = par = pah->ah_parSen;
                  pod->od_laddr = par->ar_ipg << PAGESHIFT;
                  return(NO_ERROR);
                }
              else if(vmPrvArenaReq(fl))
                {
                  pod->od_pah = pah = &(pod->od_pPTDA->ptda_ah);
                  if((laddr < pah->ah_pat->at_laddrAbsMax) &&
                     (laddr >= pah->ah_pat->at_laddrAbsMin))
                    {
                      /*
                       *  Verify that the given linear address is either
                       *  above the highest reservation in the private
                       *  arena, or below all reservations in that
                       *  arena.
                       */

                      vmAssert(laddr >= pah->ah_laddrMax ||
                               laddr <= pah->ah_laddrMin);

                      /*
                       *  If address is above the highest private
                       *  arena reservation, return info for that
                       *  reservation, else return info for the
                       *  private arena sentinel.
                       */

                      pod->od_par=par=vmArMap(pah->ah_parSen->ar_harprev);
                      if(laddr <= pah->ah_laddrMin)
                        {
                          pod->od_par = par = pah->ah_parSen;
                          pod->od_laddr = par->ar_ipg << PAGESHIFT;
                          return(NO_ERROR);
                        }
                      pod->od_laddr = par->ar_ipg << PAGESHIFT;
                      goto findobj;
                    }
                }
            }

          /*
           *  Address was expected to be within a reserved region.  Return
           *  error.
           */

          return(vmError(ERROR_INVALID_ADDRESS));
        }

      /*
       *  Base address found in a bitmap.  Scan the hash chain for the
       *  arena record that maps the base address.
       */

      har = *(pah->ah_paharHash + vmHashFunc(pat, laddr));
      while(har != HARNULL)
        {
          par = vmArMap(har);
          if(vmWithinAr(laddr, par))
            {
              /*
               *  Arena record found corresponding to base address.
               *  Return an error if the given address does not lie within
               *  the range mapped by the arena record and VMOD_NEAREST_
               *  BELOW was not specified.
               */

              if(((fl & (VMOD_NEAREST_BELOW | VMOD_BASE)) == NA) &&
                     !vmWithinAr(pod->od_laddr, par))
                {
                  return(vmError(ERROR_INVALID_ADDRESS));
                }

              /*
               *  Given linear address is valid.  Now find object
               *  and context records (if any).
               */

              goto findobj;
            }

          /*
           *  Skip to next arena record (if any) in hash chain.
           */

          har = par->ar_harhash;
        } /* end while hash chain traversal */

      /*
       *  Bitmap search unsuccessful.  Address below lowest address
       *  in arena.  Return error.
       */

      rc = vmError(ERROR_INVALID_ADDRESS);
      break;

    getsem:

      /*
       *  Take the PTDA semaphore if required.
       */

      if((fl & (VMOD_PTDA_SEM | VMOD_PTDA_SEM_TAKEN)) == VMOD_PTDA_SEM)
        {
          if((rc = VMResGetSem(vmHandleMap(VMHobPTDAOd(*pod)),
                VMGS_ERRBLOCK | VMGS_PSEUDO | VMGS_TAKESEM)) !=
                NO_ERROR)
            {
              /*
               *  An error occurred while trying to take the PTDA semaphore.
               *  If ERROR_BLOCKED was returned, repeat the whole operation
               *  from the begining, otherwise return the error.
               */

              if(rc == ERROR_BLOCKED)
                {
                  /*
                   *  If VMOD_ANY_CTXT specified, reset od->od_pPTDA to point
                   *  to the current PTDA.  There must be a valid pPTDA so
                   *  that the arena checks done at the beginning of the for
                   *  loop will work, however, any PTDA will suffice since the
                   *  address must be in the shared arena.  Note also that
                   *  the arena checks MUST be redone, since the shared arena
                   *  boundary could have moved during the block.
                   */

                  if(fl & VMOD_ANY_CTXT)
                    {
                      pod->od_pPTDA = pPTDACur;
                    }

                  continue;
                }
              return(rc);
            }
          fl |= VMOD_PTDA_SEM_TAKEN;
        }

      /*
       *  Call VMGetSem if handle semaphore action required.
       */

      if((fl & VMGS_MASK) && !(fl & VMOD_OB_GETSEM_CALLED))
        {
// b723223  if((rc = VMResGetSem(pod->od_pob, fl & VMGS_MASK)) != NO_ERROR)
// VMResGetSem() does not ensure that (ob_cslock == 0), so it is not
// dependable for calls where (fl & VMGS_SLOCKWAIT) is true.
          if((rc = VMGetSem(vmPobToHob(pod->od_pob),
                            fl & VMGS_MASK,
                            NA)) != NO_ERROR)
            {
              /*
               *  An error occurred taking the handle semaphore. If the error
               *  was ERROR_BLOCKED, restart the whole operation from the
               *  beginning, otherwise clear the PTDA semaphore (if held) and
               *  return the error.
               *
               *  B729xxx - Changed to free the PTDA semaphore if it was
               *  taken no mater what the error code was. The pointer to
               *  the ptda in the OD may be different the next time through
               *  the loop.
               */

              if(fl & VMOD_PTDA_SEM_TAKEN)
              {
                  VMClearSem(VMHobPTDAOd(*pod));
                  fl &= ~VMOD_PTDA_SEM_TAKEN;
              }

              if(rc == ERROR_BLOCKED || rc == ERROR_INTERRUPT)     // B722363
              {
                  continue;
              }

            }
          else                                                     // B722363
            {
              fl |= VMOD_OB_GETSEM_CALLED;
            }
        }

      break;
    }

  /*
   *  We now have the handle semaphore and/or have waited for the object
   *  to stabilize, or an error has been returned from getsem.  Check if
   *  we're to return an error if uvirt mapping.  Return to caller.
   */

  if(rc == NO_ERROR)
    {
      if(!(fl & VMOD_CHECK_UVIRT) || !(pod->od_pob->ob_fs & OB_UVIRT))
        {
          pod->od_laddr = par->ar_ipg << PAGESHIFT;
        }

      else
        {
          rc = vmError(ERROR_DIRECT_ACCESS_HANDLE);
        }
    }

  /*
   *  If an error occurred, clear semaphores.
   */

  if(rc != NO_ERROR)
    {
      if((fl & (VMOD_OB_GETSEM_CALLED | VMGS_TAKESEM)) ==
         (VMOD_OB_GETSEM_CALLED | VMGS_TAKESEM))
        {
          VMClearSem(VMHobOd(*pod));
        }
      if(fl & VMOD_PTDA_SEM_TAKEN)
        {
          VMClearSem(VMHobPTDAOd(*pod));
        }
    }

  /*
   *  Return result to caller.
   */

  return(rc);
}


/***LP  vmRecordOd - Record VM data for object or alias
 *
 *      This function records VM data for object by setting the bit
 *      corresponding to the base linear address in the appropriate bitmap
 *      and entering the handle of its arena record in the appropriate hash
 *      table.  If necessary, the appropriate bitmap is created or grown and
 *      its address is entered in the arena's bitmap directory.  The caller
 *      must already hold the arena semaphore.
 *
 *      WARNING  This function must remain in resident code
 *
 *      int vmRecordOd(VMAH *pah, VMAR *par)
 *
 *      ENTRY           pah             - Arena header address
 *                      par             - Pointer to arena record
 *
 *      RETURN          int             - Result code (NO_ERROR if successful)
 */

int vmRecordOd(pah, par)
VMAH                    *pah;           /* Pointer to arena header */
VMAR                    *par;           /* Arena record address */
{
  VMAT                  *pat;           /* Arena per-type info */
  VMBM                  *pbm;           /* Bitmap pointer */
  VMBM                  **ppbm;         /* Bitmap directory entry pointer */
  ulong_t               laddr;          /* Linear address */
  ulong_t               lHashNumb;      /* Hash table index */
  ulong_t               lOldSize;       /* Old table size */
  ulong_t               lbmNumb;        /* Bitmap number */
  ulong_t               lbmeNumb;       /* Bitmap entry number */
  ulong_t               lbmeNumbMaxOld; /* Old max bitmap entry number */
  ulong_t               lNumbMaxNew;    /* New max table entry number */
  ulong_t               lbmeBitNumb;    /* Bit number within bitmap entry */
  ulong_t               lbmeBit;        /* Bit to be set */
  ulong_t               flUndo;         /* Operations to be undone on error */
  ulong_t               cbData;         /* Size of heap block excl header */
  ulong_t               cbReserve;      /* Size of newly reserved space */
  VMKHBI                khbi;           /* Heap allocation info */
  VMHAR                 harHashHead;    /* Hash table head */
  int                   rc;             /* Result code */

  /*
   *  Verify that the caller holds the arena semaphore.
   */

  vmSemAssertOb(vmHandleMap(pah->ah_hob));

  pat = pah->ah_pat;
  laddr = par->ar_ipg << PAGESHIFT;
  cbReserve = par->ar_cpg << PAGESHIFT;    //108825

  for(;;)
    {
      /*
       *  Grow the hash table if the given linear address will produce a hash
       *  value beyond the end of the hash table.  Note that the hash algorithm
       *  is bounded, so growth will never exceed a certain limit.
       */

      lHashNumb = vmHashFunc(pat, laddr);
      if(lHashNumb > pah->ah_lHashNumbMax)
        {
          khbi.khbi_pbData = (char *) pah->ah_paharHash;
          lOldSize = (pah->ah_lHashNumbMax + 1) * sizeof(VMHAR);
          lNumbMaxNew = ((lHashNumb + VM_HASH_INC - 1) & ~(VM_HASH_INC - 1));
          cbData = (lNumbMaxNew + 1) * sizeof(VMHAR);
          if((rc = reallocKRHB(cbData, SSToDS(&khbi))) != NO_ERROR)
            {
              break;
            }

          /*
           *  Zero-fill portion at end of grown hash table.  Update maximum
           *  hash-table entry number that table can accommodate.  Note
           *  that hash table may have grown beyond requested size.
           */

          pah->ah_paharHash = (VMHAR *) khbi.khbi_pbData;
          cbData = vmSizeRegKRHBD(khbi.khbi_pbData);
          khbi.khbi_pbData = memset(khbi.khbi_pbData + lOldSize,
                                       0,
                                       cbData - lOldSize);
          flUndo = VM_HASH_INC;
          pah->ah_lHashNumbMax = (cbData / sizeof(VMHAR)) - 1;
          if(pah->ah_lHashNumbMax > pat->at_lHashNumbAbsMax)
            {
              pah->ah_lHashNumbMax = pat->at_lHashNumbAbsMax;
            }
        }

      /*
       *  Store arena record handle in hash table as new hash chain
       *  head.  If a hash chain already exists, link chain to arena
       *  record.
       */

      harHashHead = *(pah->ah_paharHash + lHashNumb);
      *(pah->ah_paharHash + lHashNumb) = vmParToHar(par);
      par->ar_harhash = harHashHead;

      /*
       *  Now update bitmap.  First, compute the bitmap and bitmap
       *  entry numbers from the base linear address mapped by the
       *  given arena record.  Get a pointer to the location in the
       *  bitmap directory that contains the address of the bitmap
       *  or NULL.
       */

      lbmNumb = vmbmNumbFunc(pat, laddr);
      lbmeNumb = vmbmeNumbFunc(pat, laddr);
      ppbm = pah->ah_papbm + lbmNumb;

      /*
       *  Grow bitmap directory if needed
       */

      if(lbmNumb > pah->ah_lbmNumbMax)
        {
          khbi.khbi_pbData = (char *) pah->ah_papbm;
          lOldSize = (pah->ah_lbmNumbMax + 1) * sizeof(char *);
          lNumbMaxNew = (lbmNumb + (VM_BMD_INC - 1)) & ~(VM_BMD_INC - 1);
          cbData = (lNumbMaxNew + 1) * sizeof(VMBM *);
          if((rc = reallocKRHB(cbData, SSToDS(&khbi))) != NO_ERROR)
            {
              break;
            }

          /*
           *  Fill the residual portion of the grown directory block with zeros.
           *  Count of bytes to fill = new block data size - old size
           *  Update maximum bitmap number that enlarged directory can
           *  accommodate.  Note that the grown directory may be larger
           *  than the size requested.
           */

          pah->ah_papbm = (VMBM **) khbi.khbi_pbData;
          cbData = vmSizeRegKRHBD(khbi.khbi_pbData);
          khbi.khbi_pbData = memset(khbi.khbi_pbData + lOldSize,
                                    0,
                                    cbData - lOldSize);
          pah->ah_lbmNumbMax = lNumbMaxNew;
          ppbm = pah->ah_papbm + lbmNumb;
          pbm = *ppbm = NULL;
          flUndo |= VM_BMD_INC;
          pah->ah_lbmNumbMax = (cbData / sizeof(VMBM *)) - 1;
          if(pah->ah_lbmNumbMax > pat->at_lbmNumbAbsMax)
            {
              pah->ah_lbmNumbMax = pat->at_lbmNumbAbsMax;
            }
        }

      /*
       *  Else the (non-sparse) bitmap directory contains a slot for this
       *  bitmap.  If this bitmap exists but is too small, grow it.
       */

      else
        {
          ppbm = pah->ah_papbm + lbmNumb;
          pbm = *ppbm;
          if(pbm != NULL)
            {
              lOldSize = vmSizeRegKRHBD((char *) pbm);
              lbmeNumbMaxOld = (lOldSize - sizeof(VMBMH) - sizeof(VMBME)) /
                 sizeof(VMBME);
              if(lbmeNumb > lbmeNumbMaxOld)
                {
                  lNumbMaxNew = (lbmeNumb + VM_BM_INC - 1) & ~(VM_BM_INC - 1);
                  cbData = ((lNumbMaxNew + 1) * sizeof(VMBME)) + sizeof(VMBME);
                  khbi.khbi_pbData = (char *) pbm;
                  if((rc = reallocKRHB(cbData, SSToDS(&khbi))) != NO_ERROR)
                    {
                      break;
                    }

                  /*
                   *  Fill the residual portion of the grown block with zeros.
                   *  Count of bytes to fill = new block data size - old size.
                   *  Note that the grown bitmap may be larger than the
                   *  size requested.
                   */

                  cbData = vmSizeRegKRHBD(khbi.khbi_pbData);
                  pbm = memset(khbi.khbi_pbData + lOldSize,
                               0,
                               cbData - lOldSize);
                  *ppbm = pbm = (VMBM *) khbi.khbi_pbData;
                  flUndo = VM_BM_INC;
                }
            }
        }

      /*
       *  If this bitmap does not exist, allocate it with size sufficient
       *  to accommodate the bitmap entry to be written.  Round up to
       *  the next allocation unit above.
       */

      if(pbm == NULL)
        {
          cbData = ((lbmeNumb+1+VM_BM_INC-1) & ~(VM_BM_INC-1)) * sizeof(VMBME)
            + sizeof(VMBMH);
          if((rc = allocKRHB(cbData, VMBMOWNER, SSToDS(&khbi))) != NO_ERROR)
            {
              break;
            }
          pah->ah_fl &= ~VMAH_BITMAP_BYPASS;
          cbData = vmSizeRegKRHBD(khbi.khbi_pbData);
          pbm = memset(khbi.khbi_pbData, 0, cbData);
          *ppbm = pbm = (VMBM *) khbi.khbi_pbData;
        }

      /*
       *  Now set bit in bitmap and increment count of bits set.
       *  Verify that bit is not already set.
       */

      lbmeBitNumb = vmbmeBitNumbFunc(pat, laddr);
      lbmeBit = (1L << lbmeBitNumb);
      vmAssert(!(pbm->bm_abme[lbmeNumb] & lbmeBit));
      pbm->bm_abme[lbmeNumb] |= lbmeBit;
      ++(pbm->bm_cBits);

      /*
       *  108825 - If the ending address crosses the arena bitmap boundary,
       *           then grow the bitmap to accomodate all pages in the
       *           allocation request.  Otherwise, vmGetOd may IPE when
       *           validating the bitmaps for the pages beyond the given
       *           arena boundary.
       *
       *  108825 fix starts here
       */

      if ((lbmNumb = vmbmNumbFunc(pat, laddr + cbReserve)) > pah->ah_lbmNumbMax)
        {
          lbmeNumb = vmbmeNumbFunc(pat, laddr + cbReserve);
          ppbm = pah->ah_papbm + lbmNumb;
          khbi.khbi_pbData = (char *) pah->ah_papbm;
          lOldSize = (pah->ah_lbmNumbMax + 1) * sizeof(char *);
          lNumbMaxNew = (lbmNumb + (VM_BMD_INC - 1)) & ~(VM_BMD_INC - 1);
          cbData = (lNumbMaxNew + 1) * sizeof(VMBM *);
          if((rc = reallocKRHB(cbData, SSToDS(&khbi))) != NO_ERROR)
            {
              break;
            }
          pah->ah_papbm = (VMBM **) khbi.khbi_pbData;
          cbData = vmSizeRegKRHBD(khbi.khbi_pbData);
          khbi.khbi_pbData = memset(khbi.khbi_pbData + lOldSize,
                                    0,
                                    cbData - lOldSize);
          pah->ah_lbmNumbMax = lNumbMaxNew;
          ppbm = pah->ah_papbm + lbmNumb;
          pbm = *ppbm = NULL;
          flUndo |= VM_BMD_INC;
          pah->ah_lbmNumbMax = (cbData / sizeof(VMBM *)) - 1;
          if(pah->ah_lbmNumbMax > pat->at_lbmNumbAbsMax)
            {
              pah->ah_lbmNumbMax = pat->at_lbmNumbAbsMax;
            }

          cbData = ((lbmeNumb+1+VM_BM_INC-1) & ~(VM_BM_INC-1)) * sizeof(VMBME)
            + sizeof(VMBMH);
          if((rc = allocKRHB(cbData, VMBMOWNER, SSToDS(&khbi))) != NO_ERROR)
            {
              break;
            }
          pah->ah_fl &= ~VMAH_BITMAP_BYPASS;
          cbData = vmSizeRegKRHBD(khbi.khbi_pbData);
          pbm = memset(khbi.khbi_pbData, 0, cbData);
          *ppbm = pbm = (VMBM *) khbi.khbi_pbData;
        }

      /*
       *  108825 fix ends here
       */

      /*
       *  Update the upper and lower bounds of arena reservations if
       *  necessary.  If the reservation causes hash table wrap to
       *  occur, clear the no-hash-wrap bit in the arena flags.
       */

      if(laddr < pah->ah_laddrMin)
        {
          pah->ah_laddrMin = laddr;
          if(laddr < pat->at_laddrMinNoWrap)
            {
              pah->ah_fl &= ~VMAH_NO_HASH_WRAP;
            }
        }
//108825    cbReserve = par->ar_cpg << PAGESHIFT;
      if(laddr + cbReserve > pah->ah_laddrMax)
        {
          pah->ah_laddrMax = laddr + cbReserve;
          if(laddr > pat->at_laddrMaxNoWrap)
            {
              pah->ah_fl &= ~VMAH_NO_HASH_WRAP;
            }
        }

      rc = NO_ERROR;
      break;
    } /* end for */

  /*
   *  Error handling.
   */

  return(rc);
}


/***LP  vmEraseOd - Erase VM data for object or alias
 *
 *      This function erases VM data for the object by clearing the bit
 *      corresponding to the base address in the appropriate bitmap and
 *      removing the arena record handle from the appropriate hash chain.
 *      If possible, the bitmap will be shrunk or freed.
 *
 *      WARNING  This function must remain in resident code.  Yielding
 *      is only allowed at certain points, where the consistency of the
 *      bitmap/hash table structures is guaranteed.  Note that the arena
 *      semaphore is used to serialize bitmap/hash table frees and shrinks;
 *      the semaphore is not taken in simple cases where no frees/shrinks
 *      are involved, so these simple cases must execute atomically.
 *
 *      WARNING   It is an internal error if the relevant bitmap does not
 *                exist or the bit is not set therein.
 *
 *      void vmEraseOd(VMAH *pah, VMAR *par)
 *
 *      ENTRY           pah             - Arena header address
 *                      par             - Pointer to arena record
 *
 *      RETURN          int             - Result code (NO_ERROR if successful)
 */

void vmEraseOd(pah, par)
VMAH                    *pah;           /* Pointer to arena header */
VMAR                    *par;           /* Arena record address */
{
  ulong_t               laddrBase;      /* Base laddr of object or alias */
  ulong_t               cbReserve;      /* Size of object */
  ulong_t               lbmNumb;        /* Bitmap number */
  ulong_t               lbmeNumb;       /* Bitmap entry number */
  ulong_t               lbmeBitNumb;    /* Bit number within bitmap entry */
  ulong_t               lbmeBit;        /* Bit within bitmap entry */
  ulong_t               fl = 0;         /* Flags */
  VMBM                  *pbm;           /* Address of bitmap */
  VMBM                  **papbm;        /* Pointer into bitmap directory */
  VMAR                  *parPrev;       /* Previous arena record pointer */
  VMAR                  *parNext;       /* Next arena record pointer */
  VMAT                  *pat;           /* Arena per-type info */
  VMHAR                 *pharHash;      /* Pointer to har in hash chain */
  VMHAR                 har;            /* Arena record handle */
  VMKHBI                khbi;           /* Kernel resid heap block info */
  int                   rc;             /* Callee result code */

  /*
   *  Compute bitmap number, bitmap entry number and bit position
   *  within dword bitmap entry corresponding to the given base linear
   *  address.  These are obtained by masking the linear address
   *  and shifting the result, masks being obtained from the arena's
   *  per-type info.
   */

  pat = pah->ah_pat;
  laddrBase = par->ar_ipg << PAGESHIFT;
  cbReserve = par->ar_cpg << PAGESHIFT;
  lbmNumb = vmbmNumbFunc(pat, laddrBase);
  lbmeNumb = vmbmeNumbFunc(pat, laddrBase);
  lbmeBitNumb = vmbmeBitNumbFunc(pat, laddrBase);
  lbmeBit = 1 << lbmeBitNumb;

restart:

  /*
   *  Verify that bitmap and bitmap entry exist.  Internal error of not.
   */

  TKBeginNoBlock();
  vmAssert(lbmNumb <= pah->ah_lbmNumbMax);
  papbm = pah->ah_papbm + lbmNumb;
  pbm = *papbm;
  vmAssert(pbm != NULL && lbmeNumb <= vmbmeNumbMax(pbm));

  /*
   *  If not restarting operation from beginning, clear bit in bitmap and
   *  decrement count, first verifying that the bit is set.
   */

  if(!(fl & VMOD_RESTART))
    {
      vmAssert((pbm->bm_abme[lbmeNumb] & lbmeBit) && (pbm->bm_cBits > 0));
      pbm->bm_abme[lbmeNumb] &= ~lbmeBit;
      pbm->bm_cBits--;
    }

  /*
   *  If count of bits set in bitmap goes to zero, free the bitmap from the
   *  resident heap, set bitmap pointer to NULL and shrink the bitmap
   *  directory if possible.  Note that we don't shrink bitmaps or hash
   *  tables (this can be added later if worthwhile).  Freeing or shrinking
   *  requires the arena semaphore since yields may occur.  If necessary,
   *  take the semaphore if it can be acquired without blocking.  If busy,
   *  wait and repeat operation from the beginning.
   */

  if(pbm->bm_cBits == 0)
    {
      TKEndNoBlock();
      if((rc = VMGetSem(pah->ah_hob,
                        VMGS_TAKESEM|VMGS_IGNINT|VMGS_ERRBLOCK|VMGS_PSEUDO,
                        NULL)) == ERROR_BLOCKED)
        {
          fl |= VMOD_RESTART;
          goto restart;
        }
      vmAssert(rc == NO_ERROR);

      *(papbm) = NULL;
      freeKRHB((char *) pbm);

      /*
       *  If we are freeing the highest bitmap, update the maximum and
       *  shrink the bitmap directory, but not below minimum size.
       */

      if((lbmNumb == pah->ah_lbmNumbMax) > 0)
        {
          while(*(papbm--) == NULL &&
            ((ulong_t)((char *) papbm - (char *) pah->ah_papbm) >=
              pat->at_lbdMinSize));
          papbm+=2;
          pah->ah_lbmNumbMax = papbm - pah->ah_papbm - 1;
          khbi.khbi_pbData =  (char *) pah->ah_papbm;
          reallocKRHB((pah->ah_lbmNumbMax + 1) * sizeof(VMBM *),
                       SSToDS(&khbi));
        }

      /*
       *  Clear the arena semaphore.
       */

      TKBeginNoBlock();
      VMClearSem(pah->ah_hob);
    }

  /*
   *  Unhook the arena record from its hash chain.  It is an internal
   *  error if the arena record is not found on its designated hash chain.
   */

  har = vmParToHar(par);
  pharHash = pah->ah_paharHash + vmHashFunc(pat, par->ar_ipg << PAGESHIFT);
  while(*pharHash != har)
    {
      vmAssert(*pharHash != HARNULL);
      pharHash = &(vmArMap(*pharHash)->ar_harhash);
    }
  *pharHash = par->ar_harhash;

  /*
   *  Update the upper and lower bounds of arena reservations if
   *  necessary.  Set the no hash wrap bit if the updated bounds lie
   *  within the no hash wrap limits, performing this only if the
   *  bit is not currently set.
   */

  if(laddrBase == pah->ah_laddrMin)
    {
      parNext = vmArMap(par->ar_harnext);
      pah->ah_laddrMin = ((parNext == pah->ah_parSen) ?
                         pat->at_laddrAbsMax : parNext->ar_ipg << PAGESHIFT);
      if((pah->ah_fl & (VMAH_GROW_DOWN | VMAH_NO_HASH_WRAP)) ==
          VMAH_GROW_DOWN && (pah->ah_laddrMin >= pat->at_laddrMinNoWrap))
        {
          pah->ah_fl |= VMAH_NO_HASH_WRAP;
        }
    }
  if(laddrBase + cbReserve == pah->ah_laddrMax)
    {
      parPrev = vmArMap(par->ar_harprev);
      pah->ah_laddrMax = ((parPrev == pah->ah_parSen) ?
                         pat->at_laddrAbsMin :
                         (parPrev->ar_ipg + parPrev->ar_cpg) << PAGESHIFT);
      if((!(pah->ah_fl & (VMAH_GROW_DOWN | VMAH_NO_HASH_WRAP))) &&
          pah->ah_laddrMax <= pat->at_laddrMinNoWrap)
        {
          pah->ah_fl |= VMAH_NO_HASH_WRAP;
        }
    }

  TKEndNoBlock();
}


/***EP  VMOiTOOd - Generate object data from object info
 *
 *      This function fills in an object data structure given an object
 *      info structure.  It exists to facilitate staged conversion of
 *      functions from using VMOI to use VMOD.
 *
 *      WARNING  This function must remain in resident code
 *
 *      void VMOiToOd(VMOI *poi, VMOD *pod)
 *
 *      ENTRY           *poi            - Filled in object info struct
 *      EXIT            *pod            - Filled in object data struct
 */

//void VMOiToOd(poi, pod)
//VMOI                    *poi;           /* Pointer to object info struct */
//VMOD                    *pod;           /* Pointer to object data struct */
//{
//  VMPTDAOd(*pod) = poi->oi_pPTDA;       /* Pointer to PTDA */
//  VMLaddrOd(*pod) = poi->oi_va;         /* Object lbase linear address */
//  VMPahOd(*pod) = vmGetArenaHeader(poi->oi_va >> PAGESHIFT, VMPTDAOd(*pod));
//                                        /* Pointer to arena header */
//  VMParOd(*pod) = vmArMap(poi->oi_har); /* Pointer to arena record */
//  if(VMParOd(*pod)->ar_hob != HOBNULL)
//    {
//      VMPobOd(*pod) = vmHandleMap(poi->oi_hob); /* Pointer to object record */
//    }
//  if(VMParOd(*pod)->ar_f & AR_HCO)
//    {
//      VMPcoOd(*pod) = vmCoMap(poi->oi_hco);/* Pointer to context record */
//    }
//}


/***EP  VMOdToOi - Generate object info from object data
 *
 *      This function fills in an old object info buffer given an object
 *      data buffer.  It exists to facilitate staged conversion of
 *      functions from using VMOI to use VMOD.
 *
 *      WARNING  This function must remain in resident code
 *
 *      void VMOdToOi(VMOD *pod, VMOI *poi)
 *
 *      ENTRY           od              - Filled in object data buffer
 *      EXIT            oi              - Filled in object info buffer
 */

//void VMOdToOi(pod, poi)
//VMOD                    *pod;           /* Object data */
//VMOI                    *poi;           /* Object info */
//{
//  VMOB                  *pob;           /* Pointer to object record */
//  poi->oi_va = VMLaddrOd(*pod);         /* Virtual address */
//  poi->oi_cb = VMSizeOd(*pod);          /* Size in bytes */
//  poi->oi_fsar = (ulong_t) VMParOd(*pod)->ar_f; /* Arena record flags */
//  poi->oi_har = VMHarOd(*pod);          /* Arena record handle */
//  if(poi->oi_fsar & AR_HCO)
//    {
//      poi->oi_hco = vmPcoToHco(VMPcoOd(*pod));/* Context record handle */
//    }
//  if(pod->od_par->ar_hob != HOBNULL)
//    {
//      vmAssert(pod->od_pob != NULL);
//      pob = pod->od_pob;
//      poi->oi_hob = vmPobToHob(pob);
//      poi->oi_hobowner = pob->ob_hobowner;      /* Owner i.d. */
//      poi->oi_hobmte = pob->ob_hobmte;          /* MTE handle */
//      poi->oi_fsob = pob->ob_fs;                /* Object record flags */
//      poi->oi_sel = VMSelOd(*pod);              /* GDT selector */
//      poi->oi_hobPTDA = VMHobPTDAOd(*pod);      /* PTDA handle */
//      poi->oi_cllock = pob->ob_cllock;          /* Count of long-term locks */
//      poi->oi_cslock = pob->ob_cslock;          /* Count of short-term locks */
//      poi->oi_xflags = pob->ob_xflags;          /* Extra Flags */
//      poi->oi_hal = VMParOd(*pod)->ar_hal;      /* Handle to alias record */
//      poi->oi_pPTDA = VMPPTDAOd(*pod);          /* Pointer to PTDA */
//    }
//  else
//    {
//      poi->oi_hob = HOBNULL;
//    }
//}


/***LP  vmLinkArena - Link in new arena to list.
 *
 *      This function links a newly created arena into the arena list.
 *      The arena list is doubly linked, and begins with the system
 *      arena, followed by the shared arena, followed by private arenas.
 *      The list exists to improve performance of arena scanning, avoiding
 *      the need for a TKScanTasks call and call to a worker function
 *      for every task when scanning arenas.
 *
 *      WARNING - This function must not block since the links must
 *                be updated atomically.
 *
 *      void vmLinkArena(ulong_t lat, VMAH *pah)
 *
 *      ENTRY           lat             - Arena type
 *                                          VMAT_PRV_TILED
 *                                            Private tiled arena
 *                                          VMAT_PRV_VDM
 *                                            Private VDM arena
 *                                          VMAT_SHR_TILED
 *                                            Shared tiled arena
 *                                          VMAT_SYS
 *                                            System arena
 *                      pah             - Pointer to arena header to be
 *                                        linked in.
 */

void vmLinkArena(lat, pah)
ulong_t                 lat;            /* Arena type */
VMAH                    *pah;           /* Pointer to new arena header */
{
  TKBeginNoBlock();

  /*
   *  If private arena, link into list following the shared arena.
   */

  if(lat == VMAT_PRIV_TILED || lat == VMAT_PRIV_VDM)
    {
      pah->ah_pahPrev = &ahvmShr;
      ahvmShr.ah_pahNext->ah_pahPrev = pah;
      pah->ah_pahNext = ahvmShr.ah_pahNext;
      ahvmShr.ah_pahNext = pah;
    }

  /*
   *  If shared arena, link into list following the system arena.
   */

  else if(lat == VMAT_SHR_TILED)
    {
      pah->ah_pahPrev = pah->ah_pahNext = &ahvmSys;
      ahvmSys.ah_pahNext = ahvmSys.ah_pahPrev = &ahvmShr;
    }

  /*
   * If system arena, set links to self.
   */

  else
    {
      vmAssert(lat == VMAT_SYS);
      pah->ah_pahPrev = &ahvmSys;
      pah->ah_pahNext = &ahvmSys;
    }

  TKEndNoBlock();
}


/***LP  vmUnlinkArena - Unlink an arena from list
 *
 *      This function removes an arena header from the arena list.
 *
 *      WARNING - This function must not block since the links must
 *                be updated atomically.
 *
 *      ENTRY           pah             - Pointer to arena header to
 *                                        be removed.  The arena must be
 *                                        a dead private arena.
 *      RETURN          void
 */

void vmUnlinkArena(pah)
VMAH                    *pah;           /* Pointer to arena header */
{
  TKBeginNoBlock();

  vmAssert(pah->ah_parSen == NULL ||
           vmArMap(pah->ah_parSen->ar_harnext) == pah->ah_parSen);

  pah->ah_pahPrev->ah_pahNext = pah->ah_pahNext;
  pah->ah_pahNext->ah_pahPrev = pah->ah_pahPrev;

  TKEndNoBlock();
}

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

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


/***EP  VMInitTask - Create/Initialize Per-task Memory Management Data
 *
 *      WARNING - This function is in swappable code.
 *
 *      This function manages the creation and initialization of per-task
 *      data structures used by the Selector, Virtual Memory and Page
 *      Managers.  The following structures are created:
 *
 *      Private arena including arena header, bitmap directory, minimal
 *      hash table and arena sentinel
 *      LDT (except for VDM's, which use DPMI to create an LDT if needed)
 *      Page tables
 *
 *      int VMInitTask(ptda_t pPTDA, ulong_t lat)
 *
 *      ENTRY           pPTDA           - Pointer to new task's PTDA
 *                      lat             - Task arena type
 *                                          VMAT_PRIV_TILED
 *                                          VMAT_PRIV_VDM
 *      RETURN          int             - Result code (NO_ERROR if successful)
 */

int VMInitTask(pPTDA, lat)
register ptda_t         *pPTDA;         /* Pointer to new task's PTDA */
ulong_t                 lat;            /* Arena type */
{
  VMAH                  *pah;
  int                   rc;             /* Result code */

  TKMayBlock();

  /*
   *  Verify that a non-null PTDA pointer has been given.  Verify input
   *  arena type.
   */

  MMPHPreVMInitTask()                 /* Performance Hook (20/15) */

  vmAssert(pPTDA != NULL);
  vmAssert(lat == VMAT_PRIV_TILED || lat == VMAT_PRIV_VDM);

  /*
   *  Allocate per-task Page Manager info.
   */

  if((rc = PGAllocContext(pPTDA)) == NO_ERROR)
    {
      /*
       *  Allocate per-task Virtual Memory Manager info.
       */

      pah = &(pPTDA->ptda_ah);
      if((rc = vmCreateArena(lat, pah)) == NO_ERROR)
        {
          /*
           *  Allocate the LDT if not a VDM task.
           */

          if(lat != VMAT_PRIV_VDM)
            {
              if((rc = SELInitLDT(pPTDA->ptda_handle)) == NO_ERROR)
                {
                  MMPHPostVMInitTask()          /* Performance Hook (20/95) */
                  return(NO_ERROR);
                }
            }

          /*
           *  Else this task is a VDM.  Ensure that LDT handle and
           *  address fields in PTDA are null.
           */

          else
            {
              pPTDA->ptda_ldthandle = HOBNULL;
              pPTDA->ptda_ldtaddr = NULL;
              MMPHPostVMInitTask()          /* Performance Hook (20/95) */
              return(NO_ERROR);
            }

          /*
           *  An error occurred.  Free the arena.
           */

          vmFreeArena(pah);
        }

      /*
       *  An error occurred.  Free the page tables.
       */

      PGFreeContext(pPTDA);
    }

  /*
   *  Return error to caller.
   */

  MMPHPostVMInitTask()              /* Performance Hook (20/95) */
  return(rc);
}



/***LP  vmCreateArena -  Create a new arena
 *
 *      WARNING - This function is in swappable code.
 *
 *      Given a pointer to an uninitialized arena header and an arena
 *      type, this function creates an arena.  The arena header is
 *      initialized.  An empty bitmap directory, hash table and
 *      arena sentinel are created and linked to the arena header.  The
 *      sentinel is also doubly linked to itself.
 *
 *      This function is called once for every task created.
 *
 *      int vmCreateArena(ulong_t lat, VMAH *pah)
 *
 *      ENTRY           lat             - Arena type
 *                      pah             - Pointer to uninitialized arena
 *                                        header (in PTDA or global data)
 *      RETURN          int             - Result code (NO_ERROR = success)
 */

int vmCreateArena(lat, pah)
ulong_t                 lat;            /* Arena type */
VMAH                    *pah;           /* Pointer to arena header */
{
  VMAT                  *pat;           /* Pointer to arena type info */
  VMAR                  *parSen;        /* Pointer to arena sentinel */
  int                   rc;             /* Result code */
  VMKHBI                khbi;           /* Pointer to heap block info */

  TKMayBlock();

  /*
   *  Set pointer to arena type info.  This is the part of the arena
   *  header that depends only on the type of the arena and is shared
   *  among same type arena headers to save memory.
   */

  pat = pah->ah_pat = &(atvm[lat]);

  /*
   *  Allocate a small bitmap directory.  Zerofill and save its address
   *  in the arena header.
   */

  if((rc = allocKRHB(pat->at_lbdMinSize,
                     VMBMDOWNER, SSToDS(&khbi))) == NO_ERROR)
    {
      pah->ah_papbm = memset(khbi.khbi_pbData,
                                0,
                                pat->at_lbdMinSize);
      pah->ah_lbmNumbMax = (pat->at_lbdMinSize / sizeof(char *)) - 1;

      /*
       *  Allocate a small hash table.  Zerofill and save its address in
       *  the arena header.
       */

      if((rc = allocKRHB(pat->at_lHashMinSize,
                         VMHSHOWNER, SSToDS(&khbi))) == NO_ERROR)
        {
          pah->ah_lHashNumbMax = (pat->at_lHashMinSize / sizeof(VMHAR)) - 1;
          pah->ah_paharHash =
            memset(khbi.khbi_pbData, 0, pat->at_lHashMinSize);

          /*
           *  Create sentinel arena record.  Doubly link it to itself
           *  and set base and minimum linear pagesi based on arena
           *  type number.
           */

          if((rc = vmArAlloc(SSToDS(&parSen))) == NO_ERROR)
            {
                parSen->ar_ul1 = 0;

              /*
               *  Create a pseudo-handle for the arena and save it in the
               *  arena header. The pseudo-handle is used for taking the
               *  arena semaphore.
               */

              if((rc = VMCreatePseudoHandle(pah,
                                            VMAHOWNER,
                                            &(pah->ah_hob))) == NO_ERROR)
                {
                  pah->ah_parSen = parSen;
                  parSen->ar_f = (pah == &ahvmShr) ?
                    (AR_INUSE | AR_TAGBSEN) : (AR_INUSE | AR_TAGSEN);
                  parSen->ar_harnext = parSen->ar_harprev =
                  vmParToHar(parSen);
                  parSen->ar_ipg = pat->at_laddrAbsMin >> PAGESHIFT;
                  if(lat == VMAT_PRIV_TILED || lat == VMAT_PRIV_VDM)
                    {
                      parSen->ar_ipgmax = VMAT_ST_laddrAbsMin >> PAGESHIFT;
                    }
                  else
                    {
                      parSen->ar_ipgmax = pat->at_laddrAbsMax >> PAGESHIFT;
                    }

                  parSen->ar_cpg = pat->at_cbInitBetween >> PAGESHIFT;
                  parSen->ar_hal = HALNULL;
                  parSen->ar_harlink = pat->at_harParent;

                  /*
                   *  Set first free block hint pointer to arena
                   *  sentinel.
                   */

                  pah->ah_parFree = parSen;

                  /*
                   *  Set min, max addresses at which objects are reserved.
                   */

                  pah->ah_laddrMin = pat->at_laddrInitMin;
                  pah->ah_laddrMax = pat->at_laddrInitMax;

                  /*
                   *  Initialize the arena flags
                   */

                  pah->ah_fl = pat->at_flInit;

                  /*
                   *  Link the arena onto the head of the chain.  This
                   *  linking must be in resident code.
                   */

                  vmLinkArena(lat, pah);

                  return(NO_ERROR);
                }

              /*
               *  An error occurred.  Free sentinel arena record.
               */

              vmArFree(parSen);
            }

          /*
           *  An error occurred.  Free hash table.
           */

          freeKRHB(pah->ah_paharHash);
        }

      /*
       *  An error occurred.  Free bitmap directory.
       */

      freeKRHB(pah->ah_papbm);
    }

  return(rc);
}

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

/***LP  vmFreeArena -  Free an empty arena
 *
 *      WARNING - This function is in swappable code.
 *
 *      Given a pointer to the arena header of an empty arena, this
 *      function frees the arena by releasing the arena sentinel, bitmap
 *      directory and hash table.  The caller must hold the arena
 *      semaphore.
 *
 *      This function is called once at task termination or upon exec
 *      failure to dispose of the current or child task's private arena.
 *
 *      void vmFreeArena(VMAH *pah)
 *
 *      ENTRY           pah             - Pointer to uninitialized arena
 *                                        header (in PTDA or global data)
 *      RETURN          void            - Nothing is returned.  Any error
 *                                        is an internal error .
 */

void vmFreeArena(pah)
VMAH                    *pah;           /* Pointer to arena header */
{
  VMAR                  *parSen;        /* Pointer to sentinel */
  TKMayBlock();

  /*
   *  Unlink the arena.
   */

  vmUnlinkArena(pah);

  /*
   *  Dispose of the arena sentinel.
   */

  parSen = pah->ah_parSen;
  pah->ah_parSen = NULL;
  vmArFree(parSen);

  /*
   *  Dispose of the hash table and bitmap directory.
   */

  freeKRHB((char *) pah->ah_paharHash);
  freeKRHB((char *) pah->ah_papbm);

  /*
   *  Dispose of the pseudohandle that maps the arena header.
   */

  VMFreePseudoHandle(pah->ah_hob);
}



/***EP  VMFreeTask - Free Task's Memory Management Data Structures
 *
 *      This function releases the per-task data structures used by
 *      a dying task.  These comprise the following
 *
 *      Private tiled (and later, untiled) arenas.
 *      bitmaps and hash tables for the private arenas.
 *
 *      Addressibility of the shared objects that are used to contain
 *      copies of the global and current local infosegs may optionally
 *      be retained.  If retained, the LDT of the target task is shrunk;
 *      if not retained, the LDT of the target task is freed entirely
 *      and the private arena is destroyed.
 *
 *      WARNING: This function is in swappable code!
 *
 *      void VMFreeTask(ptda_t *pPTDA)
 *
 *      ENTRY           pPTDA           - Pointer to target task's PTDA
 *                                        NOTE: must not be NULL.
 *
 *      RETURN  NONE
 *
 *      PSEUDO CODE
 */

void VMFreeTask(pPTDA)
struct ptda_s           *pPTDA;
{
  ulong_t               fl;
  register VMAH         *pah;
  VMHOB                 hobPTDA;
  int                   rc;

    MMPHPreVMFreeTask()                 /* Performance Hook (20/16) */

  TKMayBlock();

  /*
   *  Verify that a non-null PTDA pointer has been given and get the PTDA
   *  handle.  Note that the PTDA will not disappear since it is either
   *  the current task's PTDA or that of a non-running failed exec child.
   */

  vmAssert(pPTDA != NULL);
  hobPTDA = pPTDA->ptda_handle;

  /*
   *  Get the target PTDA semaphore
   */

  rc = VMGetSem(hobPTDA, VMGS_TAKESEM|VMGS_IGNINT|VMGS_PSEUDO, SSToDS(&pPTDA));
  vmAssert(rc == NO_ERROR);

  /*
   *  Free all non-long-term locked objects in the shared arena of the target
   *  task.  Leave objects with long-term locks imposed by the target task
   *  alone on this pass.
   */

  fl = NA;

  do
    {
      if((!(fl & vmft_FREE_ALL)) || (fl & vmft_LONG_LOCK_SHR))
        {
          pah = &ahvmShr;
          fl &= ~vmft_LONG_LOCK_SHR;
          if((rc = vmFreeAll(pah, pPTDA, fl & vmft_FREE_ALL)) != NO_ERROR)
            {
              /*
               *  An error occurred.  Note that vmFreeAll does not return
               *  ERROR_INTERRUPT.
               */

              vmAssert(rc == ERROR_LONGLOCK);
              fl |= vmft_LONG_LOCK_SHR;
            }
        }

      /*
       *  Empty the private tiled arena.
       */

      if((!(fl & vmft_FREE_ALL)) || (fl & vmft_LONG_LOCK_PRV))
        {
          pah = &(pPTDA->ptda_ah);
          fl &= ~vmft_LONG_LOCK_PRV;
          if((rc = vmFreeAll(pah, pPTDA, fl & vmft_FREE_ALL)) != NO_ERROR)
            {
              /*
               *  An error occurred.  Note that vmFreeAll does not return
               *  ERROR_INTERRUPT.
               */

              vmAssert(rc == ERROR_LONGLOCK);
              fl |= vmft_LONG_LOCK_PRV;
            }
        }

      fl |= vmft_FREE_ALL;
    }
  while(fl & (vmft_LONG_LOCK_PRV | vmft_LONG_LOCK_SHR));

  /*
   *  Free the private arena.
   */

  vmFreeArena(&(pPTDA->ptda_ah));

  /*
   *  Wait for any short or long-term locks on the LDT (if any) to clear and
   *  free it.  VDM tasks do not have an LDT unless one was created via a
   *  DPMI request.
   */

  if(pPTDA->ptda_ldthandle)
    {
      VMGetSem(pPTDA->ptda_ldthandle, VMGS_IGNINT | VMGS_LLOCKWAIT, NA);
      SELFreeTaskLDT(hobPTDA, NA);
    }

  /*
   *  Free per-task Page Manager data.
   */

  PGFreeContext(pPTDA);
  VMClearSem(hobPTDA);
    MMPHPostVMFreeTask()                /* Performance Hook (20/96) */
}


/***LP  vmFreeAll - Free task from private or shared arena
 *
 *      This procedure frees a task from all objects in the given arena
 *      (either the task's private arena or the shared arena).
 *
 *      WARNING: This function is in swappable code!  Note that this
 *      is possible because in walking the shared arena, the function
 *      now skips over reservations not applicable to the target
 *      context (failed exec child or dying task).
 *
 *      int vmFreeAll(VMAH *pah, ptda_t *pPTDA, ulong_t fl)
 *
 *      ENTRY           pah             - Pointer to arena header
 *                      pPTDA           - Target task PTDA address
 *                                        NOTE: must not be NULL
 *                      fl              - Flags
 *                                        - vmft_FREE_ALL
 *                                            All objects are to be freed from
 *                                            the task including locked objects
 *                                            (after waiting for locks to
 *                                             clear).  Note that the free
 *                                            will only wait for long-term
 *                                            locks to clear if they are
 *                                            imposed by the target task.
 *                                            If vmft_FREE_ALL is not given,
 *                                            these long locked objects are
 *                                            ignored and an error is returned.
 *
 *      RETURN          int             - Result code (NO_ERROR = success)
 *                                        ERROR_LONGLOCK
 *                                          One or more objects were ignored
 *                                          because they are long-term locked
 *                                          by the target task and VMFT_FREE_
 *                                          ALL was not specified.
 *
 *      PSEUDO CODE
 *
 *      CALLS           vmArMap, VMGetOd, VMFreeMemOd, VMGetSem
 *                      vmAccessLongLockInfo
 *
 *      WARNING
 *
 *      EFFECTS
 */

int                     vmFreeAll(pah,pPTDA,fl)
VMAH                    *pah;           /* Pointer to arena header */
struct ptda_s           *pPTDA;         /* PTDA address */
ulong_t                 fl;             /* flags */
{
  ulong_t               laddrBelow;     /* Virtual page number of next search */
  ulong_t               flOd;           /* Flags for VMGetOd */
  VMAR                  *par;           /* Pointer to arena record */
  register VMOD         *pod;           /* Pointer to object data */
  register VMAR         *parSen;        /* Pointer to arena sentinel */
  VMOD                  od;             /* Object data */
  VMHAR                 harSen;         /* Handle of Sentinel */
  VMHAR                 harPrev;        /* Next lower arena rec handle */
  int                   rc;             /* Result code from VMFreeMem */

  TKMayBlock();

  /*
   *  Verify flags and parameters.
   */

  vmAssert(fl == (fl & vmft_FREE_ALL));
  vmAssert(pPTDA != NULL);

  /*
   *  Check for empty arena and if so return, otherwise get base address
   *  of last object in arena.
   */

  parSen = pah->ah_parSen;
  harSen = vmParToHar(parSen);
  par = vmArMap(parSen->ar_harprev);
  if(par == parSen)
    {
      return(NO_ERROR);
    }

  pod = SSToDS(&od);
  od.od_pPTDA = pPTDA;
  od.od_laddr = par->ar_ipg << PAGESHIFT;

  /*
   *  Set flags for the VMGetOd function which is called in a loop to
   *  find the next object in the arena valid in the given context.  Note
   *  that we traverse the linked list of arena records in descending order
   *  of the region addresses they map.  If we are traversing a private
   *  arena, the arena is only changed by this thread, so we know that while
   *  we free a current reservation, the next lower reservation stays
   *  around (so the VMOD_BASE flag can be specified on the VMGetOd
   *  call).  If we are traversing the shared arena, the next lower
   *  reservation may not be connected to the target context, so may be
   *  freed during any yield occurring in the loop.
   */
//  d57702 start - for VDMs don't set the VMOD_BASE flag, if it is set vmgetod
//                 will think the object is tiled which it might not be

//  flOd = ((pah == &ahvmShr) ? (VMOD_NEAREST_BELOW | VMAC_ARENASHR):
//           (VMOD_NEAREST_BELOW | VMOD_BASE | VMAC_ARENAPRV));

// note: we could use vmVDMTask macro for the 2nd line listed below
//       but it would be inefficient

  flOd = ( (pah == &ahvmShr) ? (VMOD_NEAREST_BELOW | VMAC_ARENASHR):
          ( (pPTDACur->ptda_infoseg.LIS_ProcType == LIS_PT_REALMODE) ?
            (VMOD_NEAREST_BELOW | VMAC_ARENAPRV) :
            (VMOD_NEAREST_BELOW | VMOD_BASE | VMAC_ARENAPRV) ) );

//  d57702 end
  /*
   *  Scan the arena, freeing all attachments to objects from the process.
   */

  for(;;)
    {
      rc = VMGetOd(flOd, pod);
      vmAssert(rc == NO_ERROR || rc == ERROR_NO_OBJECT);
      if((par = od.od_par) == parSen)
        {
          break;
        }
      laddrBelow = (vmArMap(harPrev = par->ar_harprev))->ar_ipg << PAGESHIFT;

      /*
       *  Detach the target PTDA from the object.  Retry if
       *  ERROR_INTERRUPT is returned from the VMFreeMem call.
       *  VMFreeMemOd should not fail since we repeat until
       *  ERROR_INTERRUPT is no longer obtained, except where there
       *  are long-term locks or for private arena frees for arena
       *  records with AR_RELOAD set.  Note that shared arena objects
       *  not attached to the target context are skipped by VMGetOd.
       */

      while((rc = VMFreeMemOd(VMFM_OD | VMFM_TASK_TERM, pod))
            == ERROR_INTERRUPT);

      vmAssert(rc == NO_ERROR || rc == ERROR_LONGLOCK ||
                         (rc == ERROR_INVALID_ADDRESS &&
                          (VMShrArena(od.od_laddr) || vmReloadAr(par))));

      /*
       *  Wait for long-term locks to clear if directed to free everything.
       *  Otherwise, skip long-term locked object, remember that one or
       *  more long-term locks exist and return ERROR_LONGLOCK to VMFreeTask.
       */

      if(rc == ERROR_LONGLOCK || rc == ERROR_LOCKED)
        {
          fl |= vmft_LONG_LOCK;

          /*
           *  The object has long-term locks that were placed by this
           *  dying task.  If to free all objects, wait on long-term
           *  locks placed by this dying process on the object to
           *  clear.  Note that another process may free the locks, but
           *  the Page Manager requires that the address range and
           *  context for which the lock was placed (as described in the
           *  lock handle) still be valid.  Also, if I/O to a long-term
           *  locked buffer has been initiated by the dying task, the
           *  buffer must be available until the I/O completes.  If we
           *  don't have to free all objects this pass, ignore any that
           *  have long-term locks placed by the dying task.  We will wait
           *  for them on a second pass of the arena.
           */

          if((fl & vmft_FREE_ALL) &&
             VMGetSem(VMHobOd(od), VMGS_SLOCKWAIT + VMGS_LLOCKWAIT + VMGS_IGNINT,
                                                   NULL) == NO_ERROR)
            {
              continue;         /* Wait and try again */
            }
        }

      /*
       *  Set search address equal to base address of next lower neighbor
       *  in existence before the free.  Note that this neighbor may have
       *  been freed in the shared arena case if the target task was not
       *  connected to it.
       */

      od.od_laddr = laddrBelow;
      if(harPrev == harSen)
        {
          break;
        }
    }

  /*
   *  Return result code.  If ERROR_LONGLOCK is returned, VMFreeTask
   *  must perform another iteration on this arena.  One pass is made
   *  of the private and shared arenas ignoring locks and a second pass
   *  is made over arenas having long locks.
   */

  if((fl & (vmft_LONG_LOCK | vmft_FREE_ALL)) == vmft_LONG_LOCK)
    {
      rc = vmError(ERROR_LONGLOCK);
    }
  return(rc);
}

#pragma code_seg()
