/*static char *SCCSID = "@(#)vmalloc.c  6.8 92/03/11";*/
#define SCCSID  "@(#)vmalloc.c  6.8 92/03/11"

/*
 *       IBM/Microsoft Confidential
 *
 *       Copyright (c) IBM Corporation 1987, 1989
 *       Copyright (c) Microsoft Corporation 1987, 1989
 *
 *       All Rights Reserved
 *
 *  09/18/91  Khoa Dang Huynh
 *       CP20 DCR 1486  Provide selector packing capability.
 *  11/15/94  ChangeTeam Defect 81822
 *       Roll r206 defect 81822 into r207s tree.
 *  147968 5/11/96  RJM Change FSD and DD system owners to use HMTEs
 */

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

#define INCL_DOSP
#include <os2p.h>

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

#include <mi.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <pgexport.h>
#include <vmowner.h>
#include <vmexport.h>
#include <vmlocal.h>
#include <seldesc.h>
#include <selxport.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>
#include <stdlib.h>
#include <mmph.h>
#include <proc.h>
#define FOR_EXEHDR      1 /*147968 */
#include <exe386.h>       /*147968 */
#include <ldrvars.h>      /*147968 */

extern VMAR    *parGlobalShrSearchStart;  // GSR BUGBUG in vmlocal.h

/**      vmOffGSR  - Is MODIFIED by Dbgenable, Dbgdisable and       114816 *
  *                  Dbgprocterm                                    114816 *
  * vmOffGSR  =  0 means LOAD DLL's into Global Shared Region       114816 * 
  * vmOffGSR !=  0 means DO NOT LOAD DLL's into Global Shared Region 114816* 
 */
short  vmOffGSR=0;                                         /*       114816 */

/***LD  VMASkew -  Skew values for special system objects
 *
 *      These values are the page offsets at which certain system
 *      objects such as the LDT are allocated in order to optimize TLB
 *      usage.  By allocating an object aligned to a 32K boundary + n
 *      pages, the frequently used page 0 of the object accesses the
 *      TLB cache entries in group (n mod 8)
 */

ulong_t vmcpgLDTSkew = VM_SKEW_LDT;
ulong_t vmcpgGDTSkew = VM_SKEW_GDT;
ulong_t vmcpgPTOVSkew = VM_SKEW_PTOV;
ulong_t vmcpgPTOGSkew = VM_SKEW_PTOG;
/*end*/


/***LP  vmLinkOb - Link given object record to the arena record
 *
 *      WARNING: This function must remain in resident code.
 *
 *      ENTRY       pob - Pointer to object record
 *                  par - Pointer to arena record
 *      EXIT        NONE
 *
 */
void    vmLinkOb(pob, par)
VMOB    *pob;
VMAR    *par;
{
        pob->ob_hobnext = par->ar_hob;
        par->ar_hob = vmPobToHob(pob);
}


/***EP  VMReserve - allocate virtual memory
 *
 *      This function allocates a region of virtual memory
 *      of the specified size at the specified location (if given).
 *      It determines the proper arena from the flags, the
 *      PTDA handle, and the group handle.
 *      WARNING: This function updates fields of arena records that are
 *      already in the doubly linked list. This update must be done without
 *      being preempted. Therefore this function must remain in resident code.
 *
 *      VMReserve (cpgreq, fl, pptda, ipgreq, hobOwner, ppar)
 *
 *      ENTRY   cpgreq          - requested size in pages
 *              fl              - allocation flags
 *              pptda           - PTDA pointer - Must be non-null except
 *                                for system arena reservations
 *              ipgreq          - requested starting page number
 *              hobOwner        - Special owner (system arena only)
 *              ppar            - place to put arena record pointer
 *      RETURN  int             - error code (NO_ERROR if successful)
 *
 *      From the flags and the PTDA and group record handles,
 *      find the arena sentinel for the appropriate arena, and
 *      call vmReserveSub to do the actual work.
 *
 *      CALLS   vmReserveSub, vmGetArenaHeader,
 *              VMRecordOd, VMVirtFree
 *
 *      WARNING This function may block.
 *
 *      EFFECTS Called functions have side effects.
 */

int                     VMReserve(cpgreq,fl,pptda,ipgreq,hobOwner,ppar)
ulong_t                 cpgreq;         /* Number of pages wanted */
ulong_t                 fl;             /* Allocation flags */
register struct ptda_s  *pptda;         /* PTDA pointer */
ulong_t                 ipgreq;         /* Starting virtual page */
VMHOB                   hobOwner;       /* Special system owner */
register VMAR           **ppar;         /* Place to put arena record */
{
    register VMAH       *pah;           /* Arena header array pointer */
    int                 rc;             /* Return code */
    VMCO                *pco;           /* Pointer to context record */
    VMHOB               hobptda;        /* Target task's PTDA handle */

    /*
     * In a VDM context, first 4Meg is managed by VDM Manager which
     * never makes 'allocate at any address' calls. We shouldn't be
     * putting any other allocations (originating from File System etc..)
     * in there either. So if this is a 'LOCANY' request in the
     * private address space of a VDM, convert the request to
     * an 'above 4Meg' one.
     */

    if (vmVDMTask(pptda) && ((fl & VMAC_LOCMASK) == VMAC_LOCANY) &&
     ((fl & VMAC_ARENAMASK) == VMAC_ARENAPRV))
      {
        vmAssert(VMAC_LOCANY == NULL);
        fl |= VMAC_LOCABOVE;
        ipgreq = _4MEG >> PAGESHIFT;
      }


    pah = &ahvmShr;                     /* Assume shared untiled
                                           arena allocation */
    switch((int)(fl & VMAC_ARENAMASK))  /* Switch on arena type */
      {
        case VMAC_ARENASYS:             /* System arena */
          pah = &ahvmSys;               /* Get pointer to arena header
                                             and do the allocation */
          if((rc = vmReserveSub(cpgreq,fl,ipgreq,pah,hobOwner,ppar))==NO_ERROR)
            {
              (*ppar)->ar_hobptda = HOBNULL;    /* No context if sysarena */
            }
          break;

        case VMAC_ARENAPRV:             /* Private arena */
          pah = &(pptda->ptda_ah);
                                        /* Assume private untiled arena */
          /* Drop through */

        case VMAC_ARENASHR:             /* Shared arena */
          vmAssert(pptda != NULL);
          hobptda = pptda->ptda_handle; /* Get target PTDA handle */
          if((rc = vmReserveSub(cpgreq,fl,ipgreq,pah,hobptda,ppar)) != NO_ERROR)
            {
              break;
            }
          if(vmShrArenaReq(fl))
            {                           /* If shared arena */
              if(vmShrDataReq(fl))
                {                       /* if also shared contents */
                  if((rc = vmCoAlloc(SSToDS(&pco))) == NO_ERROR)
                    {                   /* Create context record and
                                           store PTDA pseudo-handle in it */
                      pco->co_fb = (uchar_t)(((((fl & (PG_RESIDENT|PG_UVIRT)) ?
                        (fl & ~PG_16M) : fl) >> CO_PERMSHFT) & CO_PERMMASK) |
                        CO_CREATOR);
                      pco->co_hobptda = hobptda;

//                    pco->co_hconext = (*ppar)->ar_hco;
//                    (*ppar)->ar_hco = vmPcoToHco(pco);

                      (*ppar)->ar_f |= AR_HCO;
                      vmLinkCo(pco, *ppar);
                    }
                }
              else vmAssert((*ppar)->ar_hobptda == HOBNULL);
                                        /* For shared arena private contents
                                           the context will be stored in the
                                           ob_hobowner field for the object
                                           record for the context.  This
                                           assertion ensures that ar_hobptda
                                           was initialized to NULL in
                                           vmReserveSub */
            }
          else (*ppar)->ar_hobptda = pptda->ptda_handle;
                                        /* For private arena, (shared or
                                           private contents), the context
                                           is saved in the arena record */
          break;
      }

    /*
     *  If this is a pre-reservation request for a future allocation
     *  mark the arena record to allow such allocations
     */

    if(rc == NO_ERROR && vmPreResReq(fl))
      {
        (*ppar)->ar_f |= AR_RELOAD;
      }
    return(rc);                         /* Return code */
}



/***LP  vmReserveSub - allocate virtual memory only
 *
 *      This function allocates a region of virtual memory
 *      of the specified size in the specified arena at
 *      the specified location (if given).
 *
 *      vmReserveSub (cpgreq, fl, ipgreq, pah, hobptda, ppar)
 *
 *      ENTRY   cpgreq          - requested size in pages
 *              fl              - allocation flags
 *              ipgreq          - requested starting page number
 *              pah             - pointer to arena header
 *              hobptda         - PTDA handle for target task
 *              ppar            - place to return arena record pointer
 *      RETURN  int             - error code (NO_ERROR if successful)
 *      EFFECTS Takes the arena semaphore.
 *
 *      Preallocate an arena record so that we won't block later.
 *      Determine which end of the specified arena to search from.
 *      Find an appropriate free region.  Link the new arena record
 *      into the arena and fill in its size and virtual address.
 *      Return a pointer to the new arena record.
 *
 *      CALLS   vmArAlloc, vmArMap, vmFreeSearchDown, vmFreeSearchUp,
 *              vmArFree, vmParToHar
 *
 *      WARNING This function may block, but it must not block
 *              from the time it allocates an arena record until
 *              after the record is inserted into the arena list.
 *              Therefore this function must remain in resident code.
 *
 *      EFFECTS Allocates an arena record if necessary and inserts it in
 *              the appropriate list.
 *
 *              NOTE:   The only case where an arena record need not be
 *              allocated is where the specified range of addresses map
 *              instance data in one or more contexts.  In that case, the
 *              existing arena record to which the chain of instance
 *              object records is attached is used.
 */

typedef int (*pfnSearch)(ulong_t cpgReq,
                         ulong_t fl,
                         ulong_t ipgReq,
                         VMAR *par,
                         VMAH *pah,
                         VMAR **ppar);

int                     vmReserveSub(cpgreq,fl,ipgreq,pah,hobptda,ppar)
ulong_t                 cpgreq;         /* Requested size in pages */
ulong_t                 fl;             /* Flags */
ulong_t                 ipgreq;         /* Requested starting page number */
VMAH                    *pah;           /* Pointer to arena header */
VMHOB                   hobptda;        /* PTDA handle for target context.
                                         * or hobOwner if system arena
                                         * PTDA handle only needed for
                                         * instance data in the shared arena
                                         */

VMAR                    **ppar;         /* Place to put arena record pointer */
{
    VMHOB               hobOwner = hobptda;
    pfnSearch           freesearch;     /* Free region search function */
    VMHAR               harnew;         /* Handle of new arena record */
    ulong_t             ipgfree;        /* Free page number */
    VMAR                **pparhint;     /* Hint pointer pointer. */
    VMAR                *parfound;      /* Pointer to arena record found
                                           by search routine */
    register VMAR       *parabove;      /* Pointer to arena record above
                                           the one found by the search */
    register VMAR       *parnew;        /* New arena record pointer */
    ulong_t             flOd;           /* VMGetOd flags */
    int                 rc;             /* Return code */
    register VMAR       *parSen;        /* Pointer to arena sentinel */
    VMOB                *pob;           /* Pointer to object record */
    VMOD                od;             /* Object data */
    int                 rcsem = ERROR_INTERRUPT;
                                        /* Return code from VMGetSem */
    ulong_t             cpgSkew = 0;    /* Page offset for TLB optim. */

    parSen = pah->ah_parSen;            /* Point to sentinel */
    if(VMTiledPg(parSen->ar_ipg))       /* If arena in tiled area */
    {                                   /* If tiled allocation wanted */
        /*
         *  Default alignment is page alignment for VM objects.
         */

        if((fl & ~VMAC_ALIGNMASK) == 0)
        {
            fl |= VMAC_ALIGNPAGE;
        }

        /*
         *  Allocation request is for tiled memory.  If not a VDM request
         *  convert page aligned requests to tiled selector aligned requests.
         */

        if(vmAlignPgReq(fl) && !vmVDMReq(fl))
        {
            fl = (fl & ~VMAC_ALIGNMASK) | VMAC_ALIGNSEL;
        }

        /*
         *  Round byte count to match granularity of alignment
         */

        cpgreq = vmCeil(cpgreq,aulvmAlignMask[vmAlign(fl)]);
    }

    /*
     *  If a specific base address was requested, verify that the given
     *  address matches the alignment.
     */

    vmAssert(!vmLocSpecReq(fl) ||
      ipgreq == vmFloor(ipgreq,aulvmAlignMask[vmAlign(fl)]));

    /*
     *  Pre-allocate an arena record.  Normally this is used for the
     *  allocation request, but in certain cases, an existing arena record
     *  is used instead.
     */

    if((rc = vmArAlloc(ppar)) != NO_ERROR)
    {
        return(rc);
    }
    parnew = *ppar;
    parnew->ar_ul1 = 0;
    parnew->ar_f = AR_INUSE;

    /*
     *  Search the specified arena for a suitable free address range.  If
     *  the request is for a specific location, use the bitmap to find the
     *  nearest arena record.  Otherwise, perform a linear search through
     *  the arena starting at the arena record pointed to by the free block
     *  hint pointer.
     */

    if(vmLocSpecReq(fl))
    {
        /*
         *  This is a specific request.  Search the bitmap to find the nearest
         *  arena record whose base address is below or equal to the specified
         *  address.  Take the arena semaphore if it is free, otherwise wait
         *  for a free to occur, do a repeat search of the bitmap and try to
         *  take the semaphore again.  We need to know that the arena has not
         *  changed while we waited for the semaphore.
         */

        do
        {
            od.od_laddr = ipgreq << PAGESHIFT;
            od.od_pPTDA = vmSysArenaReq(fl) ?
              NULL : VMGetPTDAAddr(hobptda);

            /*
             *  Setup flags for arena search.  For shared arena, specify
             *  that object may be mapped in any context.
             */

            flOd = (fl & VMAC_ARENAMASK) | VMOD_NEAREST_BELOW;
            if(vmShrArenaReq(fl))
            {
                flOd  |= VMOD_ANY_CTXT;
            }
            if((rc = VMGetOd(flOd, SSToDS(&od))) != NO_ERROR)
            {
                /*
                 *  If ERROR_INTERRUPT, fail the request immediately.
                 */

                if(rc == ERROR_INTERRUPT)
                {
                    vmArFree(parnew);
                    if(rcsem == NO_ERROR)
                    {
                        VMClearSem(pah->ah_hob);
                    }
                    return(rc);
                }

                /*
                 *  The only other errors allowed are ERROR_INVALID_ADDRESS
                 *  (because no arena records map addresses at or below the
                 *  given address in the arena) or ERROR_NO_OBJECT (because
                 *  an objectless reservation was found).
                 */

                vmAssert(rc == ERROR_INVALID_ADDRESS || rc == ERROR_NO_OBJECT);
                if(rc == ERROR_INVALID_ADDRESS)
                {
                    od.od_par = parSen;
                }
                rc = NO_ERROR;
            }
            parfound = od.od_par;
        }
        while((rcsem = VMGetSem(pah->ah_hob,
                             VMGS_TAKESEM|VMGS_ERRBLOCK|VMGS_PSEUDO,
                             SSToDS(&pob))) == ERROR_BLOCKED);

        /*
         *  If we have taken the arena semaphore successfully, check if the
         *  specified address region overlaps with an existing arena record.
         *  The check should be done on both ends of the region we are
         *  going to reserve. If overlap occurs, set rc=ERROR_BUSY.  We check
         *  for this further below.
         */

        parabove = vmArMap(parfound->ar_harnext);
        if(rcsem == NO_ERROR && ((parfound->ar_ipg + parfound->ar_cpg > ipgreq)
          || ((ipgreq + cpgreq) > ((vmArIsSentinel(parabove)) ?
                parabove->ar_ipgmax : parabove->ar_ipg))))
        {
            /*
             *  The arena record found overlaps the region we want and
             *  parfound points to the conflicting arena record.  The
             *  allocation will fail except in two cases described below.
             */

            vmAssert(parfound->ar_harprev != HARNULL);
            rc = ERROR_BUSY;
        }
    }
    else
    {
        /*
         *  Not a specific request.  Start search from hint of first free
         *  block.
         */

        pparhint = &pah->ah_parFree;

        /*
         *  Select the appropriate arena search routine.  Search the private
         *  arena upwards, search the shared and system arenas downwards.
         */

        freesearch = vmFreeSearchDown;
        if(vmShrArenaReq(fl))
        {
            /*
             *  If this request is for protected memory in the shared arena,
             *  search down starting from the arena sentinel.  (The free block
             *  hint pointer always points to an arena record in the unprotected
             *  region.)
             */
/* GSR start */
            if(fl & VMAC_PROT)
            {
                pparhint = &parProtectSearchStart;
            }
            else
            {
               if(fl & VMAC_PACKSHR)
               {
                   vmAssert(VMPackedMem);
                   pparhint = &parPackedSearchStart;
               } else {
                  // if (not (writeable or API allocation)) or
                  //    the object is shrinkable start the
                  //    search in the Global shared region
                  if ( ((fl & (PG_W | VMAC_OBAPI)) == 0 ) && vmOffGSR == 0) /* 114816 */
                  {
                     pparhint = &parGlobalShrSearchStart;
                  } else {
                     if ( PGGlobalFlags & PG_SYS_CONSTRAINED  &&
                         (fl & VMAC_OBSHRINK  || cpgreq > 0x100) ) {
                           pparhint = &parGlobalShrSearchStart;
                     }
                  }
               }
            }
/* GSR end */
        }
        else if(!vmSysArenaReq(fl))
        {
            freesearch = vmFreeSearchUp;
        }
        else /* System arena request */
        {
            vmAllocTLBAdjust(SSToDS(&cpgSkew), hobOwner, SSToDS(&fl));
            cpgreq += cpgSkew;
        }

        /*
         *  Search the arena to find space, returning a pointer to the
         *  arena record mapping the preceding busy region (sentinel if none).
         *  Take the arena semaphore if it is free (even if the arena search
         *  fails), otherwise wait for a free to occur, do a repeat search of
         *  the bitmap and try to take the semaphore again.  We need to know
         *  that the arena has not changed while we waited for the semaphore.
         */

        do
        {
            /*
             *  If we are interrupted, we need to respond quickly.  For
             *  any other error, we continue to take the semaphore and
             *  retry the search if we blocked.
             */

            if((rc=(*freesearch)(cpgreq,
                                 fl,
                                 ipgreq,
                                 *pparhint,
                                 pah,
                                 SSToDS(&parfound))) == ERROR_INTERRUPT)
            {
                vmArFree(parnew);
                if(rcsem == NO_ERROR)
                  {
                    VMClearSem(pah->ah_hob);
                  }
                return(rc);
            }
        }
        while((rcsem = VMGetSem(pah->ah_hob,
                    VMGS_TAKESEM|VMGS_ERRBLOCK|VMGS_PSEUDO,
                    SSToDS(&pob)))
          == ERROR_BLOCKED);
    }

    /*
     *  If the taking of the arena semaphore is interrupted, return
     *  ERROR_INTERRUPT.  No other error should occur if we get here.
     */

    if(rcsem != NO_ERROR)
    {
        vmAssert(rcsem == ERROR_INTERRUPT);
        vmArFree(parnew);
        return(rcsem);
    }

    /*
     *  The semaphore was taken and the result of the search is final.
     */


    if(rc != NO_ERROR)
    {
        /*
         *  The amount of address space requested is not entirely free or a
         *  specific request conflicts with existing reservations.  We fail
         *  the allocation except in permitted cases where we are loading out
         *  of pre-reserved memory.
         */

        cpgreq -= cpgSkew;

        if(rc == ERROR_BUSY)
        {

            /*
             *  Check for address space conflict allowed because the request
             *  is an allowed allocation out of reserved address space.  There
             *  are three cases in which this is allowed
             *
             *  A Loading huge objects out of their pre-reserved address space
             *  B Reloading freed shared-content DLL objects (via DOSLOADMODULE)
             *  C Creating second and subsequent instances of DLL private
             *    data.
             *
             *  NOTE:  Second or subsequent attaches to a partially freed
             *         program EXE module (DOSEXECPGM calls to run second and
             *         subsequent copies of the program) will restore freed
             *         EXE objects in the new contexts, but in this case, it
             *         is an internal error if the specific private arena
             *         addresses needed to attach/load freed objects are
             *         not available in the new contex's arena.
             *
             *  Cases A,B are distinguished from other conflicts by two flags
             *
             *  - The conflicting arena record must have AR_RELOAD set for
             *    such allocations to be allowed.  If not set, an error is
             *    returned.
             *  - The request must specify VMAC_RELOAD to indicate that it is
             *    a reload request.  Since reload requests are internally
             *    generated (by the loader), it is an internal error if
             *    any validation checks fail.
             *
             *  Case C is distinguished from other conflicts by verifying
             *  that the conflicting arena record has an object record owned
             *  by the same MTE for which the present allocation is being
             *  made.
             */

            if(vmReloadReq(fl))
            {
                /*
                 *  This is Case A or B - a loader request to allocate out of
                 *  virtual memory  reserved for reloading an MTE object or
                 *  loading a chunk of a huge item.  Assert that:
                 *
                 *  - Specific base address request
                 *  - Conflicting arena record is regular
                 *  - Conflicting arena record is marked AR_RELOAD
                 *  - Base vpage in AR matches requested base vpage
                 *  - requested vpages <= vpage count in AR
                 *
                 *  NOTE:  Since only the loader can free virtual memory
                 *  reserved for reloads (at garbage collection), it is an
                 *  internal error if the memory is not still reserved.
                 */

                vmAssert(vmLocSpecReq(fl) &&
                         vmRegAr(parfound) &&
                         vmReloadAr(parfound) &&
                         ipgreq == parfound->ar_ipg &&
                         cpgreq <= parfound->ar_cpg);

                if(cpgreq < parfound->ar_cpg)           /* If huge alloc */
                {
                    vmAssert(cpgreq == (_64K >> PAGESHIFT));

                    /*
                     *  Request is to allocate 64K chunk out of address
                     *  space reserved for huge item.  Release 64K from
                     *  the reserved address space by converting the
                     *  arena record that pre-reserves the address space
                     *  into the arena record that will be used for the
                     *  allocation request.  Use the newly allocated arena
                     *  record for mapping the remainder of the pre-reserved
                     *  address space.
                     *
                     *  NOTE:  This means that we do not have to remove the
                     *         old locate table info for the pre-reserving
                     *         arena record and insert the new locate table
                     *         info for the updated pre-reserving arena
                     *         record; a situation which may be impossible
                     *         to undo if we run out of memory when trying to
                     *         add the new locate table info.
                     *
                     *  Copy the pre-reserving arena record to the newly
                     *  allocated arena record.  Set the base of the new
                     *  record to reflect the remaining amount of pre-reserved
                     *  virtual memory.  Try to insert locate table info for
                     *  the new record.  If error, free new record and return.
                     */

                    *parnew = *parfound;
                    parnew->ar_ipg += cpgreq;
                    parnew->ar_cpg -= cpgreq;
                    if((rc = vmRecordOd(pah, parnew)) != NO_ERROR)
                    {
                        vmArFree(parnew);
                        return(rc);
                    }

                     /*
                      *  Convert the old pre-reserving arena record into
                      *  one that describes the 64K chunk being allocated.
                      *  Link the old record to the new pre-reserving arena
                      *  record.  Return pointer to the old record instead of
                      *  the new record.  Note that its location information
                      *  is still valid.
                      */

                    parabove = vmArMap(parfound->ar_harnext);
                    parfound->ar_harnext = vmParToHar(parnew);
                    parabove->ar_harprev = parfound->ar_harnext;
                    parnew->ar_harprev = vmParToHar(parfound);
                    parfound->ar_cpg = cpgreq;
                }
                else                            /* Reloading non-huge object */
                {
                    /*
                     *  Request to allocate the whole of a region reserved
                     *  for reloading.  Dispose of the arena record pre-
                     *  allocated at the beginning of this routine and use
                     *  the existing arena record mapping the reserved space
                     *  for the object.
                     */

                    vmArFree(parnew);
                }

                /*
                 *  Set flags (except AR_RELOAD) in converted arena record.
                 *  BUGBUG IDB 11/20/90   - Note that the setting of
                 *  permissions in the arena record should only be necessary
                 *  for private and system arena objects, since shared arena
                 *  objects have permissions in object or context records.
                 *  I have found at lest one instance, however, in which
                 *  code was incorrectly accessing permissions from the
                 *  arena record instead of using the VMGetObPerm procedure,
                 *  so I am temporarily leaving this code as unconditional.
                 */

                /* 94936 - The folks in Austin have uncovered a window where
                   we can be affecting an arena record for a shared arena
                   object (eg. a object out of DOSCALL1.DLL) while another
                   context is in the process of realloc'ing it. That window
                   still exists, but the following hack should prevent the
                   IPE in selReallocSub.
                */
                {
                BOOL fSelMapped = parfound->ar_f & AR_SELMAP;

                parfound->ar_f = (AR_INUSE | ((((fl & (PG_RESIDENT|PG_UVIRT)) ?
                  (fl & ~PG_16M) : fl) << VMAC_PSHIFT) & AR_PERMISSION));

                if ( fSelMapped )
                  parfound->ar_f |= AR_SELMAP;
                }

                /*
                 *  Set the AR_RELOAD bit if retention of the reserved address
                 *  space (after freeing the last reference to the object) has
                 *  been requested so that a future attach to a containing
                 *  DLL or EXE module can reload the object at the same
                 *  linear address range to retain validity of fixups.
                 */

                if(vmRetainReq(fl))
                {
                    parfound->ar_f |= AR_RELOAD;
                }

                *ppar = parfound;
                rc = NO_ERROR;
            }
            else                                /* Not reload, try instance */
            {
                /*
                 *  ERROR_BUSY was returned for a specific allocation
                 *  request other than from memory pre-reserved for huge or
                 *  reserved for reloading.  Check for Case C.
                 */

                if(vmLocSpecReq(fl) && vmShrArenaReq(fl) && !vmShrDataReq(fl))
                {
                    /*
                     *  Request is for private data in shared arena at
                     *  specific addresses.  Check for Case C - second or
                     *  subsequent allocation of instance DLL data.  We error
                     *  if any of the following checks fail.
                     *
                     *  -  Conflicting arena record is shared arena - d76221
                     *  -  Conflicting arena record is regular
                     *  -  Conflicting arena record maps requested
                     *     address range exactly
                     *  -  Conflicting arena record has an object record
                     *     that has a non-null MTE handle.
                     *  -  Object record is not for shared data
                     *  -  Object record exists for specified context
                     */

                    if(VMShrArena((parfound->ar_ipg) << PAGESHIFT) && /*d76221*/
                       vmRegAr(parfound) &&
                       ipgreq == parfound->ar_ipg &&
                       cpgreq == parfound->ar_cpg &&
                       parfound->ar_hob != HOBNULL &&
                       ((pob = vmHandleMap(parfound->ar_hob))->ob_hobmte
                         != HOBNULL) &&
                       (pob->ob_hobmte ==             // 81822
                         (vmHandleMap(parnew->ar_hob))->ob_hobmte) &&
                       !vmShrOb(pob))
                    {
                        vmAssert(hobptda != HOBNULL);

                        /*
                         *  Scan chain of instance object records linked to
                         *  the arena record to verify that there is not
                         *  already an instance for the current context.
                         */

                        while(pob->ob_hobnext != HOBNULL)
                        {
                            if(pob->ob_hobowner == hobptda) break;
                            pob = vmHandleMap(pob->ob_hobnext);
                            vmAssert(!vmShrOb(pob));
                        }

                        if(pob->ob_hobowner != hobptda)
                        {
                            /*
                             *  There is no instance of the object already in
                             *  the current context.  Allow the allocation
                             *  (i.e. creation of an instance in the current
                             *  context by changing the error reply from
                             *  the arena search routine to NO_ERROR
                             *  Free up the pre-allocated arena record, since
                             *  we will return a pointer to the existing arena
                             *  record instead.
                             */

                            vmArFree(parnew);
                            *ppar = parfound;
                            rc = NO_ERROR;
                        }
                    }
                }
            }
        }
    }
    else                                /* rc from search == NO_ERROR */
    {                                   /* Address space is free */
        /*
         *  A region of the requested size and, if required, specific
         *  base address or base address above a specific address is
         *  available.  This must not be a loader request to allocate
         *  out of reserved memory.
         *
         *  The search routine will have returned a pointer to the arena
         *  record mapping the highest reserved region (if any) below the
         *  available region.  If the request is for a non-specific
         *  allocation, carve the required amount out from the top or bottom
         *  of the available region depending on the direction of search
         *
         *  For system and shared arenas, carve from the top end of the
         *  free region,  For private arenas, carve from the bottom end
         *  of the free region, taking into account whether address range
         *  not below a given address is required.
         */

        vmAssert(!vmReloadReq(fl));
        if(!vmLocSpecReq(fl))
        {                               /* If not specific address request */
            if(vmShrArenaReq(fl) || vmSysArenaReq(fl))
            {                           /* If carving from top */
                vmAssert(parfound->ar_harnext != HARNULL);
                                        /* Must have link */
                parabove = vmArMap(parfound->ar_harnext);
                                        /* Map next arena record */
                ipgreq = ((parabove == parSen)?
                        parabove->ar_ipgmax: parabove->ar_ipg) - cpgreq;
                                        /* Get starting page number */
                ipgreq = vmFloor(ipgreq,aulvmAlignMask[vmAlign(fl)]);
                                        /* Round down appropriately */
                if (cpgSkew != 0)
                  {
                    ipgreq += cpgSkew;
                    cpgreq -= cpgSkew;
                  }
            }
            else                        /* Else searching upwards */
            {
                /*
                 *  Take the lower bound of free space, adjusted upwards if
                 *  necessary to take into account VMAC_LOCABOVE and align-
                 *  ment requests, as the base of the region to be allocated.
                 *  Note that the region has already been chosen so that
                 *  it is large enough to allow this adjustment.
                 */
                vmAssert(cpgSkew == 0);
                ipgfree = parfound->ar_ipg + parfound->ar_cpg;
                if((!vmLocAboveReq(fl)) || ipgreq < ipgfree)
                {
                    ipgreq = ipgfree;
                }

                ipgreq = vmCeil(ipgreq,aulvmAlignMask[vmAlign(fl)]);
                                        /* Round up appropriately */
            }
        }

        /*
         *  Initialize fields in newly created arena record except fields
         *  linking it to neighbors.
         *
         *  NOTE:  The object record to which this arena record points
         *         is known at the VMAllocMem or other calling level.
         */

        parnew->ar_cpg = cpgreq;        /* Set size */
        parnew->ar_ipg = ipgreq;        /* Set base page */
        parnew->ar_hob = HOBNULL;       /* No object record handle yet */
        parnew->ar_hobptda = HOBNULL;   /* No context yet */
        parnew->ar_harlink = HARNULL;   /* No related arena records yet */
        parnew->ar_hal = HALNULL;       /* No alias handle yet */

        /*
         *  Insert location information for the new arena record
         *  If no error occurs, link the arena record to its neighbors
         *  and adjust the free region hint pointer as appropriate.
         */

        if((rc = vmRecordOd(pah, *ppar)) == NO_ERROR)
        {
            parnew->ar_harnext = parfound->ar_harnext;
                                        /* Set forward link */

            parnew->ar_harprev = vmParToHar(parfound);
                                        /* Set back link */

            /*
             *  Set flags (except AR_RELOAD) in arena record.
             *  BUGBUG IDB 11/20/90   - Note that the setting of
             *  permissions in the arena record should only be necessary
             *  for private and system arena objects, since shared arena
             *  objects have permissions in object or context records.
             *  I have found at lest one instance, however, in which
             *  code was incorrectly accessing permissions from the
             *  arena record instead of using the VMGetObPerm procedure,
             *  so I am temporarily leaving this code as unconditional.
             */

            parnew->ar_f = (AR_INUSE | ((((fl & (PG_RESIDENT|PG_UVIRT)) ?
                (fl & ~PG_16M) : fl) << VMAC_PSHIFT) & AR_PERMISSION));
                                        /* Set permission bits */
            /*
             *  Set the AR_RELOAD bit if retention of the reserved address
             *  space (after freeing the last reference to the object) has
             *  been requested so that a future attach to a containing
             *  DLL or EXE module can reload the object at the same
             *  linear address range to retain validity of fixups.
             */

            if(vmRetainReq(fl))
            {
                parnew->ar_f |= AR_RELOAD;
            }                           /* Indicate to retain for reloading */

            harnew = vmParToHar(parnew);/* Get handle of new record */

            vmAssert(parfound->ar_harnext != HARNULL);
                                        /* Link must not be null */

            vmArMap(parfound->ar_harnext)->ar_harprev = harnew;
                                        /* Link into list */

            parfound->ar_harnext = harnew;
        }
    }

    /*
     *  If any error occurred after allocation of the arena record, except
     *  permitted reservation conflicts, free the arena record.
     */

    if(rc != NO_ERROR)
    {
        vmArFree(parnew);               /* Free the arena record */
    }

    /*
     *  Clear the arena semaphore.  The arena reservation is complete.
     */

    VMClearSem(pah->ah_hob);
    return(rc);
}


/***LP  vmRecalcShrBound - Recompute lower shared arena bound
 *
 *      This procedure scans all the private arenas to find the current
 *      upper bound of private linear addresses in use.  Optionally, the
 *      shared arena base can be expanded down to become the maximum of
 *      that upper bound and the absolute minimum shared arena address
 *      allowed.
 *
 *      WARNING:        This function must not block since it scans a
 *                      linked list that may be updated.
 *
 *      vmRecalcShrBound(ulong_t fl, ulong *pladdrMinShr)
 *
 *      ENTRY           fl              - Flags
 *                                          vmrb_SHR_GROW_DOWN
 *                                            Grow the shared arena down
 *                                            to the computed maximum
 *                                            of all private arena
 *                                            reservations.
 *
 *                      pladdrMinShr     - Pointer to location to receive
 *                                         new bound.  Note that the new
 *                                         bound is stored in the arena
 *                                         header if vmrb_SHR_GROW_DOWN
 *                                         is specified.
 *
 *      RETURN          NONE
 *
 *      CALLS           NONE
 *
 *      WARNING         This procedure assumes that it will not block and
 *                      that no private arena will change while it is running.
 *
 *      EFFECTS         Changes the base address and size of the
 *                      Boundary Sentinel between the private and shared arenas.
 */

void                    vmRecalcShrBound(fl, pladdrMinShr)
ulong_t                 fl;             /* Flags */
ulong_t                 *pladdrMinShr;  /* Pointer for returning max prv */
{
  ulong_t               laddrMaxPrv;    /* Highest private address in use */
  ulong_t               laddrMinShr;    /* New low bound for shared arena */
  VMAH                  *pah;           /* Arena pointer */
  VMAR                  *parSen;        /* Sentinel arena record */

  /*
   *  Scan linked list of all private arenas.  Private arenas (if any)
   *  follow the shared arena on the list.
   */

  pah = ahvmShr.ah_pahNext;
  laddrMaxPrv = 0L;
  TKBeginNoBlock();
  while(pah != &ahvmSys)
    {
      /*
       *  Skip dead or empty arenas.
       */

      if(pah->ah_parSen == NULL ||
         vmArMap(pah->ah_parSen->ar_harnext) == pah->ah_parSen)
        {
          pah = pah->ah_pahNext;
          continue;
        }

      /*
       *  Update max of private arena address upper bounds if the one
       *  in this arena is larger than the maximum to date.
       */

      if(laddrMaxPrv < pah->ah_laddrMax)
        {
          laddrMaxPrv = pah->ah_laddrMax;
        }
      pah = pah->ah_pahNext;
    }

  /*
   *  Verify that the computed upper bound for private arena addresses in
   *  use does not exceed the minimum shared arena address
   *  If the computed max private linear address is below the absolute minimum
   *  for the shared arena, clamp to that value.
   */

  vmAssert(laddrMaxPrv <= ahvmShr.ah_laddrMin);
  laddrMinShr = (laddrMaxPrv >= VMAT_ST_laddrAbsMin) ?
    laddrMaxPrv : VMAT_ST_laddrAbsMin;

  /*
   *  Return the new min shared arena bound if requested.
   */

   if(pladdrMinShr != NA)
     {
       *pladdrMinShr = laddrMinShr;
     }

  /*
   *  If requested, also adjust the shared arena base downwards to
   *  the current maximum of private arena addresses.
   */

  if(fl & vmrb_SHR_GROW_DOWN)
    {
      /*
       *  Update fields in boundary sentinel for shared arena.  Set
       *  new base page and page count.
       */

      parSen = ahvmShr.ah_parSen;
      parSen->ar_ipg = laddrMinShr >> PAGESHIFT;
      parSen->ar_cpg = (ahvmShr.ah_laddrMin - laddrMinShr) >> PAGESHIFT;
    }

  TKEndNoBlock();
}


/***LP  vmFreeSearchUp - search upwards for free region
 *
 *      Given the request parameters and a pointer to an arena record from
 *      which to start searching, this function searches the arena upwards to
 *      find a free region that satisfies the request.  If successful, a pointer
 *      to the arena record preceding the free region is returned.
 *
 *      vmFreeSearchUp(cpgReq, fl, ipgReq, par, pah, ppar)
 *
 *      ENTRY           cpgReq          - Requested size in pages
 *                      fl              - Allocation flags
 *                      ipgReq          - Requested mimimum base virtual page
 *                                        number (when fl has VMAC_LOCABOVE
 *                                        set)
 *                      par             - Pointer to starting arena record
 *                      pah             - Pointer to arena header
 *                      ppar            - Place to return arena record pointer
 *      RETURN          int             - Return code (NO_ERROR if successful)
 *
 *      Walk the arena from the starting point upwards until either the
 *      request is satisfied or it is determined that the request cannot be
 *      satisfied.  If the arena is growable upwards and the boundary sentinel
 *      is reached, try to move it in order to satisfy the request.
 *      WARNING:This function may block if arena growth is attempted. This
 *      function walks the doubly linked arena records without taking
 *      semaphores. This walk should not be preempted, therefore this function
 *      must remain in resident code.
 *
 *
 *      CALLS           vmArMap, vmMoveSentinelUp
 *
 *
 *      EFFECTS         May grow the arena and block.
 */

int                     vmFreeSearchUp(cpgReq,fl,ipgReq,par,pah,ppar)
ulong_t                 cpgReq;         /* Requested size in pages */
ulong_t                 fl;             /* Flags */
ulong_t                 ipgReq;         /* Requested page number */
VMAR                    *par;           /* Pointer to starting arena record */
VMAH                    *pah;           /* Pointer to arena header */
VMAR                    **ppar;         /* Place to put arena record pointer */
{
  ulong_t               rc;             /* Return code from vmFreeCheck */
  register VMAR         *parNext;       /* Pointer to next arena record */
  register ulong_t      ipgBusy;        /* Upper bound of free region */
  register ulong_t      ipgMinFree;     /* Lower bound of free region */

  /*
   *  Loop to find free space.  The outer do loop repeats the search if
   *  the arena needed to be grown but was not because a yield took place.
   */

  do
    {
      do
        {
          /*
           *  On each pass, par points to an arena record.  We will examine
           *  the free region above that mapped by the arena record.  Verify
           *  that the arena record has an upper link to another arena
           *  record.  Get pointers (ipgMinFree and ipgBusy) to lower and
           *  upper bounds of the free region above that mapped by given arena
           *  record.  Verify that arena record maps addresses below base of
           *  next arena record.
           */

          vmAssert(par->ar_harnext != HARNULL);
          parNext = vmArMap(par->ar_harnext);
          ipgMinFree = par->ar_ipg + par->ar_cpg;
          ipgBusy = vmSentAr(parNext)? parNext->ar_ipgmax: parNext->ar_ipg;
          vmAssert(ipgBusy >= ipgMinFree);

          /*
           *  If there is no free space between this arena record and the
           *  adjacent record, move the free region hint pointer up.
           */

          if(ipgBusy == ipgMinFree &&
             par == pah->ah_parFree &&
             !vmSentAr(parNext))
            {
              pah->ah_parFree = parNext;
            }

          /*
           *  Switch on location requirements.  Note that specific address
           *  requests should not be using this routine.
           */

          vmAssert(vmLocAnyReq(fl) || vmLocAboveReq(fl));
          switch((ulong_t)(fl & VMAC_LOCMASK))
            {
              /*
               *  If region base to be above or equal to a given address,
               *  check that there is sufficient space in the free region if
               *  we carve from the top.  Check that the upper bound of the
               *  free region is not less than the minimum possible upper
               *  bound for the requested region.  If this fails, continue
               *  the search, else drop through to the VMAC_LOCANY case to
               *  check size of free region (above the given address).
               */

              case VMAC_LOCABOVE:
                if(ipgBusy < ipgReq + cpgReq)
                  {
                    par = parNext;
                    continue;
                  }
                if(ipgMinFree < ipgReq)
                  {
                    ipgMinFree = ipgReq;
                  }

              /*
               *  If region can be anywhere, round up lower base to alignment
               *  needed and check size.
               */

              case VMAC_LOCANY:
                ipgMinFree = vmCeil(ipgMinFree,aulvmAlignMask[vmAlign(fl)]);
                if(ipgMinFree + cpgReq <= ipgBusy)
                  {
                    *ppar = par;
                    return(NO_ERROR);
                  }
            }

          /*
           *  Move up to next arena record.
           */

          par = parNext;
        }
      while(!vmSentAr(par));            /* Do until sentinel reached */

      /*
       *  At this point, we can still satisfy the request if we
       *  can adjust the boundary sentinel.
       */
    }
  while((rc = vmMoveSentinelUp(cpgReq,fl,ipgReq,par,ppar)) == ERROR_BLOCKED);
  return(rc);
}




/***LP  vmFreeSearchDown - Search downwards for free region
 *
 *      Given the request parameters and a pointer to an arena record from
 *      which to start searching, this function searches the arena downwards to
 *      find a free region that satisfies the request.  If successful, a pointer
 *      to the arena record preceding the free region is returned.
 *      WARNING:This function may block if arena growth is attempted. This
 *      function walks the doubly linked arena records without taking
 *      semaphores. This walk should not be preempted, therefore this function
 *      must remain in resident code.
 *
 *      vmFreeSearchDown(cpgReq, fl, ipgReq, par, pah, ppar)
 *
 *      ENTRY           cpgReq          - Requested size in pages
 *                      fl              - Allocation flags
 *                      ipgReq          - Requested mimimum base virtual page
 *                                        number (when fl has VMAC_LOCABOVE
 *                                        set)
 *                      par             - Pointer to starting arena record
 *                      pah             - Pointer to arena header
 *                      ppar            - Place to return arena record pointer
 *      RETURN          int             - Return code (NO_ERROR if successful)
 *
 *      Walk the arena from the starting point downwards until either the
 *      request is satisfied or it is determined that the request cannot be
 *      satisfied.  If the arena is growable downwards and the boundary sentinel
 *      is reached, try to move it in order to satisfy the request.
 *
 *      CALLS           vmArMap, vmMoveSentinelDown
 *
 *      EFFECTS         NONE
 */

int                     vmFreeSearchDown(cpgReq,fl,ipgReq,par,pah,ppar)
ulong_t                 cpgReq;         /* Requested size in pages */
ulong_t                 fl;             /* Flags */
ulong_t                 ipgReq;         /* Requested page number */
VMAR                    *par;           /* Pointer to starting arena record */
VMAH                    *pah;           /* Pointer to arena header */
VMAR                    **ppar;         /* Place to put arena record pointer */
{
  register ulong_t      ipgBusy;        /* Upper bound of free region */
  register ulong_t      ipgMinFree;     /* Lower bound of free region */
  register VMAR         *parPrev;       /* Pointer to previous arena record. */

  /*
   *  Loop to find free space.
   */

  do
    {
      /*
       *  On each pass, par points to an arena record.  We will examine the
       *  free region below that mapped by the arena record.  Verify that the
       *  arena record has a lower link to another arena record.  Get pointers
       *  (ipgMinFree and ipgBusy) to lower and upper bounds of the free region
       *  below that mapped by given arena record.  Verify that arena record
       *  maps addresses above mapped by next lower arena record.
       */

      vmAssert(par->ar_harprev != HARNULL);
      parPrev = vmArMap(par->ar_harprev);
      ipgMinFree = parPrev->ar_ipg + parPrev->ar_cpg;
      ipgBusy = (vmSentAr(par)) ? par->ar_ipgmax : par->ar_ipg;
      vmAssert(ipgBusy >= ipgMinFree);

      /*
       *  If there is no free space between this arena record and the
       *  adjacent record, move the free region hint pointer down.
       */

      if(ipgBusy == ipgMinFree && par == pah->ah_parFree && !vmSentAr(parPrev))
        {
          pah->ah_parFree = parPrev;
        }

      /*
       *  Switch on location requirements.  Note that specific address requests
       *  should not be using this routine.
       */

      vmAssert(vmLocAnyReq(fl) || vmLocAboveReq(fl));
      switch((ulong_t)(fl & VMAC_LOCMASK))
        {
          /*
           *  If region base to be above or equal to a given address, check that
           *  there is sufficient space in the free region if we carve from the
           *  top.  Check that the upper bound of the free region is not less
           *  than the minimum possible upper bound for the requested region.
           *  If this fails, stop the search, else drop through to the
           *  VMAC_LOCANY case to check size of free region (above the given
           *  address).
           */

          case VMAC_LOCABOVE:
            if(ipgBusy < ipgReq + cpgReq)
              {
                break;
              }
            if(ipgMinFree < ipgReq)
              {
                ipgMinFree = ipgReq;
              }

          /*
           *  If region can be anywhere, round up lower base to alignment needed
           *  and check size.
           */

          case VMAC_LOCANY:
            ipgMinFree = vmCeil(ipgMinFree,aulvmAlignMask[vmAlign(fl)]);
            if(ipgMinFree + cpgReq <= ipgBusy)
              {
                *ppar = parPrev;
                return(NO_ERROR);
              }
        }

      /*
       *  Move down to next lower arena record.
       */

      par = parPrev;
    }
  while(!vmSentAr(par));                /* Do until sentinel reached */

  /*
   *  At this point, we can still satisfy the request if we
   *  can adjust the boundary sentinel.
   */

  return(vmMoveSentinelDown(cpgReq,fl,ipgReq,parPrev,ppar));
}


/***LP  vmMoveSentinelUp - try to find free space by moving sentinel up
 *
 *      This function tries to find free space to satisfy an
 *      allocation request by moving the base of the boundary
 *      sentinel up far enough to allow the allocation to succeed.
 *      Note that the function will return ERROR_BLOCKED if any yield
 *      would be necessary.  In this case, the calling code must retry
 *      the growth.
 *      WARNING: This function must remain in resident code.
 *
 *      int vmMoveSentinelUp(ulong_t cpgReq,
 *                           ulong_t fl,
 *                           ulong_t ipgReq,
 *                           VMAR *parSen,
 *                           VMAR **ppar)
 *
 *      ENTRY           cpgReq          - Requested size in pages
 *                      fl              - Allocation flags
 *                      ipgReq          - Requested virtual page number
 *                                        (optional)
 *                      parSen          - Pointer to sentinel
 *                      ppar            - Place to return arena record pointer
 *      RETURN          int             - Result code (NO_ERROR if successful)
 *                                          ERROR_BLOCKED - A yield took
 *                                            place; no growth attempted.
 *
 *      Compute the lowest address not needed and the lowest
 *      address not allowed.  If the former is less than or
 *      equal to the latter, then recompute the base address
 *      and size of the region described by the boundary sentinel,
 *      and satisfy the request.
 *
 *      CALLS           vmArMap
 *
 *      EFFECTS         Changes the location and size of the region
 *                      described by the boundary sentinel.  Changes
 *                      the maximum size in the "ordinary" sentinel.
 */

int                     vmMoveSentinelUp(cpgReq, fl, ipgReq, parSen, ppar)
ulong_t                 cpgReq;         /* Requested size in pages */
ulong_t                 fl;             /* Flags */
ulong_t                 ipgReq;         /* Requested page number */
register VMAR           *parSen;        /* Pointer to sentinel */
VMAR                    **ppar;         /* Place to put arena record pointer */
{
  ulong_t               ipgFree;        /* Free page number */
  ulong_t               ipgMax;         /* Maximum allowable page number */
  register VMAR         *parParent;     /* Parent arena record pointer */
  VMAH                  *pah;           /* Upper arena header pointer */
  VMOB                  *pob;           /* Upper arena pseudo object record */
  register VMAR         *parLast;       /* Last record in arena */
  int                   rc;             /* Result code */

  /*
   *  The specified arena record must be a sentinel and must have an
   *  upwards link to another arena (called the "upper arena").  Currently
   *  the only such combination possible is that the lower arena is a
   *  private tiled arena and the upper arena is the unique shared tiled
   *  arena.
   */

  vmAssert(vmSentAr(parSen));
  vmAssert(parSen->ar_harlink != HARNULL);

  /*
   *  Follow upwards link and verify that it points to a boundary sentinel.
   */

  parParent = vmArMap(parSen->ar_harlink);
  vmAssert(vmBdrySentAr(parParent));

  /*
   *  Verify that the lower arena sentinel has a non-null previous record
   *  pointer.  This record is either the sentinel or the record mapping
   *  the highest address range in use in the lower arena.  Get upper
   *  bound of busy addresses.
   */

  vmAssert(parSen->ar_harprev != 0);
  parLast = vmArMap(parSen->ar_harprev);
  ipgFree = parLast->ar_ipg + parLast->ar_cpg;

  /*
   *  If a specific address or address not less than a specified value
   *  is wanted, adjust free pointer upwards.
   */

  if((vmLocSpecReq(fl) || vmLocAboveReq(fl)) && ipgFree < ipgReq)
    {
      ipgFree = ipgReq;
    }

  /*
   *  If not a specific address adjust according to type of rounding
   *  needed.
   */

  if(!vmLocSpecReq(fl))
    {
      ipgFree = vmCeil(ipgFree,aulvmAlignMask[vmAlign(fl)]);
    }

  /*
   *  Add on size of object to compute address of first unneeded page.
   */

  ipgFree += cpgReq;

  /*
   *  Take the upper arena's semaphore to protect other upper arena
   *  allocations from being undermined.  We do not need to protect against
   *  other lower arena growths because the code here does not yield
   *  during boundary sentinel interrogation/update.
   */

  pah = vmGetArenaHeader(parParent->ar_ipg, pPTDACur);
  if((rc = VMGetSem(pah->ah_hob,
                    VMGS_TAKESEM | VMGS_ERRBLOCK | VMGS_PSEUDO,
                    SSToDS(&pob))) != NO_ERROR)
    {
      return(rc);
    }

  /*
   *  Find the maximum allowable page from the boundary sentinel of
   *  the upper arena.  This is the upper limit of the "no-man's land"
   *  mapped by that sentinel.  It is an internal error if the maximum
   *  lower arena address exceeds this upper limit.
   */

  ipgMax = parParent->ar_ipg + parParent->ar_cpg;
  vmAssert(parSen->ar_ipgmax <= ipgMax);

  /*
   *  If there is not enough room for the new object even by moving
   *  the shared arena sentinel up the maximum, return error.  The dummy
   *  do loop facilitates error handling.
   */

  do
    {
      if(ipgFree > ipgMax)
        {
          rc = ERROR_NOT_ENOUGH_MEMORY;
          break;
        }

      /*
       *  Increase the maximum limit of this lower arena to the upper bound
       *  of the address range that the new object will occupy.  Set the upper
       *  arena sentinel base to the upper bound of the new object if the upper
       *  bound of the new object is greater than the existing base.
       *
       *  NOTE (1)  Increasing the lower arena upper limit (ar_ipgmax)
       *            prevents the upper arena from encroaching on the address
       *            range needed for the new object, because a grow-down
       *            operation will not go below the maximum of all lower arena
       *            upper limits.  This is true even though the object has not
       *            yet been allocated out of the arena.
       *
       *  NOTE (2)  We leave the base unchanged if it would decrease, since
       *            some other lower arena may have an object with address
       *            range reaching up to the old base.  In general, therefore,
       *            we have:
       *
       *                lower ar_ipgmax <= shared ar_ipg
       */

      if(ipgFree > parParent->ar_ipg)
        {
          /*
           *  Upper bound of new object exceeds current upper arena base.
           *  Nibble some of the "no-man's" land.
           */

          parParent->ar_ipg = ipgFree;
          parParent->ar_cpg = ipgMax - ipgFree;
        }

      /*
       *  Set upper bound for this lower arena equal to upper bound for
       *  new object.
       */

      parSen->ar_ipgmax = ipgFree;

      /*
       *  Return pointer to last arena record in lower arena.  The new
       *  object's arena record will be linked later via the last record's
       *  ar_harnext pointer.
       */

      *ppar = parLast;

    } while(FALSE);

  /*
   *  Clear upper arena semaphore and return
   */

  VMClearSem(pah->ah_hob);
  return(rc);
}


/***LP  vmMoveSentinelDown - try to find free space by moving sentinel down
 *
 *      This procedure tries to find free space to satisfy an
 *      allocation request by moving the base of the boundary
 *      sentinel down far enough to allow the allocation to succeed.
 *
 *      vmMoveSentinelDown (cpgreq, fl, ipgreq, parSen, ppar)
 *
 *      ENTRY   cpgreq          - requested size in pages
 *              fl              - allocation flags
 *              ipgreq          - requested virtual page number (optional)
 *              parSen          - pointer to boundary sentinel
 *              ppar            - place to return arena record pointer
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *
 *      CALLS   vmArMap, vmRecalcShrBound
 *
 *      WARNING This function assumes that there will be no
 *              contention for the boundary sentinel.  It must
 *              not block.  It must remain in resident code.
 *
 *      EFFECTS Changes the location and size of the region
 *              described by the boundary sentinel.
 */

int                     vmMoveSentinelDown(cpgreq,fl,ipgreq,parSen,ppar)
ulong_t                 cpgreq;         /* Requested size in pages */
ulong_t                 fl;             /* Flags */
ulong_t                 ipgreq;         /* Requested page number */
register VMAR           *parSen;        /* Pointer to boundary sentinel */
VMAR                    **ppar;         /* Place to put arena record pointer */
{
    ulong_t             ipgbusy;        /* First busy page after sentinel */
    register ulong_t    ipgwanted;      /* Wanted base page */
    register VMAR       *parnext;       /* Next arena record pointer */

    /*
     *  The system arena cannot be grown, so return error if the
     *  system sentinel was passed in.
     */

    if(parSen == ahvmSys.ah_parSen)           // B724698
    {
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    vmAssert(vmBdrySentAr(parSen));
                                        /* Must be a boundary sentinel */
    vmAssert(parSen->ar_harnext != HARNULL);    /* Must have forward link */
    parnext = vmArMap(parSen->ar_harnext);
                                        /* Map the next record */
    ipgbusy = (parnext == parSen)? parSen->ar_ipgmax: parnext->ar_ipg;
                                        /* First busy page after sentinel */
    if(!vmLocSpecReq(fl))
      {                                 /* If not specific address */
        if(cpgreq > ipgbusy)
          {
            return(ERROR_NOT_ENOUGH_MEMORY);
                                        /* Request is unsatisfiable */
          }
        ipgwanted = vmFloor(ipgbusy - cpgreq,aulvmAlignMask[vmAlign(fl)]);
                                        /* Compute wanted page number */
        if(ipgwanted > ipgbusy || (vmLocAboveReq(fl) && ipgwanted < ipgreq))
          return(ERROR_NOT_ENOUGH_MEMORY);
                                        /* Fail if below minimum desired */
      }
    else                                /* Else specific address wanted */
      {
        if(ipgreq + cpgreq > ipgbusy) return(ERROR_BUSY);
                                        /* Fail if desired unavailable */
        ipgwanted = ipgreq;             /* Set specific address */
      }
    if(parSen->ar_ipg > ipgwanted)      /* If not low enough */
      {
#ifdef  VMSTRICT
        ipgreq = parSen->ar_ipg;        /* Remember lowest available address */
#endif

        /*
         *  Recompute the boundary by scanning all private arenas looking
         *  for the maximum upper bounds of the last object in each one.
         *  NOTE:  It is an internal error if the boundary increases, since
         *  this would mean that some private arena object did not lie entirely
         *  below it.
         */

        vmRecalcShrBound(vmrb_SHR_GROW_DOWN, NA);
        vmAssert(ipgreq >= parSen->ar_ipg);
                                        /* Must not have increased */
        if(parSen->ar_ipg > ipgwanted) return(ERROR_NOT_ENOUGH_MEMORY);
                                        /* Fail if still to high */
      }
    parSen->ar_cpg = ipgwanted - parSen->ar_ipg;
                                        /* Reduce size of no-man's-land */
    *ppar = parSen;                     /* Return pointer to sentinel */
    return(NO_ERROR);                   /* Success */
}


/***LP vmAllocTLBAdjust - Adjust size of reservation up or down
 *
 *      This function adjusts the size of a reservation so that
 *      a chunk can be carved with a specific alignment that avoids
 *      usage of virtual addresses that map to certain TLB slots.
 *      It is called once
 *      WARNING: This function must not block, since it is called from
 *      the 'must not block' portion of vmReserveSub.
 *
 *      void vmAllocTLBAdjust(ulong_t *pcpgSkew,
 *                            VMHOB hobOwner, ulong_t,
 *                            ulong_t *pfl)
 *
 *      ENTRY           pcpgSkew        - Pointer to  adjust amount
 *                      hobOwner        - Owner of new memory
 *                      pfl             - Pointer to allocation flags
 *      EXIT            *pcpgSkew       - Contains adjust amount
 */

void vmAllocTLBAdjust(pcpgSkew, hobOwner, pfl)
ulong_t                 *pcpgSkew;      /* Pointer to adjustment amount */
VMHOB                   hobOwner;       /* Owner of memory */
ulong_t                 *pfl;           /* Pointer to allocation flags */
{
  switch(hobOwner)
    {
      case SELLDTOWNER:
        *pcpgSkew = vmcpgLDTSkew;
        break;

      case SELGDTOWNER:
        *pcpgSkew = vmcpgGDTSkew;
        break;

      case PTOVIRTOWNER:
        *pcpgSkew = vmcpgPTOVSkew;
        break;

      case PTOGDTOWNER:
        *pcpgSkew = vmcpgPTOGSkew;
        break;

      default:
        *pcpgSkew = 0;
        break;
    }

  /*
   *  If a skew factor is specified, specify 64Kb alignment.
   *  Since the system arena grows down, we will carve from the top
   *  of the augmented region, hence the base page will be offset from
   *  the 64K boundary by the skew amount.
   */

  if(*pcpgSkew != 0)
    {
          *pfl = ((*pfl & ~VMAC_ALIGNMASK) | VMAC_ALIGNSEL);
    }
}


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

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


/***EP  VMAllocMem - Reserve virtual memory and commit initial physical storage
 *
 *      This function is used to allocate virtual address space and optionally
 *      commit physical storage to a contiguous region of the address space
 *      beginning at the base of the object.  To commit or decommit any
 *      subrange of physical storage to/from an object subsequently, use the
 *      VMSetMem function.
 *      WARNING: This function is in swappable code.
 *
 *      NOTE:   For reservations in the shared arena (whether for shared
 *              or private data), a reservation for an address space that
 *              already exists will be treated as a null operation provided
 *              that (1) the rounded address range exactly matches that of
 *              an object, and (2) commit has been specified and is successful.
 *              This simplifies calling code in that the same VMAlloc call
 *              may be made for the first and subsequent instances of an object.
 *
 *      int VMAllocMem(ulong_t cb,
 *                     ulong_t cbCommit,
 *                     ulong_t fl,
 *                     VMHOB hobPTDA,
 *                     VMHOB hobOwner,
 *                     VMHOB hobMTE,
 *                     ulong_t flSelMgr,
 *                     ulong_t blkno,
 *                     VMAC *pac)
 *
 *      ENTRY           cb              - Size of virtual memory object in
 *                                        bytes.  This may not be changed
 *                                        subsequently.  Note that the size
 *                                        will be rounded up to page
 *                                        granularity, by adding the offset of
 *                                        the specific base address (if any)
 *                                        and rounding the result up to a
 *                                        multiple of the page size (4K).
 *                      cbCommit        - Amount of physical storage to be
 *                                        committed to contiguous virtual
 *                                        space  starting from the base of the
 *                                        object.  The physical storage may
 *                                        optionally be requested to be
 *                                        contiguous by specifying PG_CONTIG
 *                                        in the allocation flags.
 *                                        This parameter is ignored if heap
 *                                        allocation is requested.
 *                      fl              - Allocation flags (see notes below)
 *                      hobPTDA         - Target context's PTDA pseudo-
 *                                        handle.  This is relevant for
 *                                        private and shared arena allocations
 *                                        only; for those arenas the value
 *                                        HOBNULL may be specified to mean
 *                                        that the target is the current
 *                                        context.
 *                                        This parameter must be 0 if heap
 *                                        allocation is requested.
 *                      hobOwner        - The owner of the memory.  Permitted
 *                                        values depend on the arena in which
 *                                        the allocation is being made, and
 *                                        the type of data.  A HOBNULL value
 *                                        means the current PTDA's
 *                                        pseudo-handle.
 *
 *                                        Arena Type    Permitted Values
 *                                        Heap          Allocate from the heap
 *                                        System        A system owner
 *                                                      (see vmowner.h)
 *                                        Shared        If the data is shared,
 *                                                      the owner may be an
 *                                                      MTE handle, PTDA pseudo
 *                                                      handle or HOBNULL.
 *                                                      Otherwise, the owner
 *                                                      must be a PTDA pseudo-
 *                                                      handle or HOBNULL.
 *                                        Private       A PTDA pseudo-handle
 *                                                      or HOBNULL.
 *                      hobMTE          - MTE handle (if loader object)
 *                                        Must be 0 for heap allocations
 *                      flSelMgr        - Selector Manager allocation flags
 *                      blkno;          - Loader block no. for loader objects
 *                                        Physical address for uvirt objects.
 *                                        Must be 0 for heap allocations.
 *                      pac             - Address of VMAllocMem output buffer.
 *                                        Parameters that can be written to by
 *                                        VMAllocMem appear in this buffer.
 *                                        Note that in some cases, (ac_va) and
 *                                        (ac_sel) values placed in the buffer
 *                                        may be read also.  The buffer has
 *                                        the following format:
 *
 *                                      struct vmac_s
 *                                        {
 *                                          ulong_t ac_va;
 *                                          - Base virtual address
 *                                            A specific base virtual address
 *                                            or minimum base virtual address
 *                                            may be requested by setting its
 *                                            value here and specifying
 *                                            VMAC_LOCSPECIFIC/VMAC_LOCANY in
 *                                            the allocation flags.  The base
 *                                            address is rounded down to the
 *                                            nearest page boundary.  In all
 *                                            cases, the actual base address
 *                                            allocated to the object is
 *                                            returned.
 *                                          VMHOB ac_hob;
 *                                          - Object's handle
 *                                            The handle allocated to the object
 *                                            is returned.
 *                                          SEL ac_sel;
 *                                          - Selector
 *                                            A specific selector may be
 *                                            requested by setting its value
 *                                            here and specifying VMAC_SELALLOC
 *                                            in the allocation flags and
 *                                            SELAL_SPECIFIC in the Selector
 *                                            Manager allocation flags.
 *                                        }
 *
 *      EXIT            int             - Result code (NO_ERROR if successful)
 *
 *      Verify parameters.  Allocate an object record.  Allocate
 *      the virtual space.  Allocate the physical memory.  Assign and
 *      map selectors.
 *
 *      CALLS           vmObAlloc, VMReserve, vmPobToHob, PGAlloc,
 *                      VMGetPTDAAddr, vmParToHar, VMVirtFree, vmObFree,
 *                      vmGetArenaHeader
 *
 *
 *      EFFECTS         Initializes object and arena record.  See also
 *                      called functions.
 *
 *      NOTES on flags usage and calling VMAllocMem
 *
 *      (1)     Specify the type of arena using the following flags
 *
 *              VMAC_ARENASYS   - System arena
 *              VMAC_ARENASHR   - Shared arena
 *              VMAC_ARENAPRV   - Private arena
 *              VMAC_ARENAHEAP  - Heap allocation
 *
 *      (2)     Give information about the base virtual address of the object
 *
 *              VMAC_LOCANY     - Any location in the specified arena is OK
 *              VMAC_LOCSPECIFIC- A specific base address is requested
 *                                  Not valid for heap allocs.
 *              VMAC_LOCABOVE   - Base address must be above that given
 *                                  Not valid for heap allocs.
 *
 *      (3)     Indicate the alignment of the base address (4K page alignment
 *              is enforced; input addresses, byte counts will be rounded
 *              automatically so that the actual region allocated contains
 *              the whole of its first and last page)
 *
 *              VMAC_ALIGNPAGE  - Page granularity
 *              VMAC_ALIGN64K   - 64K granularity (Not valid for heap
 *                                allocations).
 *
 *      (4)     Indicate whether selector(s) are required
 *
 *              VMAC_SELALLOC   - Selectors are needed
 *
 *      (5)     Specify properties of object. Not valid for heap allocations.
 *
 *              VMAC_OBSHRINK   - Object is shrinkable (physical commitment
 *                                only, not virtual address space)
 *              VMAC_OBHUGE     - Object is a huge object being allocated
 *                                on behalf of a DOSALLOCHUGE API call.
 *
 *      (6)     Specify Page Manager information
 *
 *              Select one of each of the PG_ state flags and zero or more
 *              PG_ permission and modifier flags.  For more details,
 *              see the Page Manager Exported definitions file pgexport.h
 *
 *      (7)     Specify Selector Manager information
 *
 *              See selxport.h
 */

int                     VMAllocMem(cb, cbCommit, fl, hobPTDA, hobOwner,
                                   hobMTE, flSelMgr, blkno, pac)
ulong_t                 cb;             /* Virtual size of object in bytes */
ulong_t                 cbCommit;       /* Count of bytes to commit */
ulong_t                 fl;             /* Allocation flags */
VMHOB                   hobPTDA;        /* Target PTDA pseudo-handle */
VMHOB                   hobOwner;       /* Owner */
VMHOB                   hobMTE;         /* MTE handle */
ulong_t                 flSelMgr;       /* Selector Manager flags */
ulong_t                 blkno;          /* Loader block no. */
VMAC                    *pac;           /* Read/write parameter buffer */
{
  ulong_t               va;             /* Rounded base virtual address */
  ulong_t               ipg;            /* Base virtual page number */
  ulong_t               cpg;            /* Count of pages requested */
  register ulong_t      cpgCommit;      /* Count of pages to commit */
  VMAR                  *par;           /* Arena record pointer */
  register VMOB         *pob;           /* Object record pointer */
  VMOB                  *pobNonReg;     /* Object rec ptr (not in reg var) */
  register struct ptda_s *pPTDA;        /* PTDA pointer */
  ulong_t               flLocal;        /* Private VMAllocMem flags */
  VMHKH                 hkh;            /* Kernel heap handle */
  int                   rc;             /* Return code */

  MMPHPreVMAllocMem();                  // Performance Hook

  /*
   *  BUGBUG: People are reckless about setting PG_X, PG_R flags. This
   *  messes up later VMAttach'es when default permissions are used.
   *  So review the selmgr flags, and make sure to set PG_X and
   *  possibly PG_R flag if it should have been set.
   */
/* GSR start */
    /*
     *  Prevent based dlls from putting writable data in the global
     *   shared region
     */

    if( vmLocSpecReq(fl) && (fl & PG_W) &&  (pac->ac_va > MINGLOBALSHRLADDR) &&
          (pac->ac_va < MAXGLOBALSHRLADDR) && (!(fl & VMAC_RELOAD)) )
    {
        return(ERROR_BUSY);
    }

/* GSR end */

  if((fl & VMAC_SELALLOC) && (flSelMgr & SELAL_CODE))
    {
      fl |= PG_X;
      if(flSelMgr & SELAL_R)
        {
          fl |= PG_R;
        }
    }

  /* KDH - If VMAC_PACKSHR flag is specified, VMAC_ARENASHR flag should
   *      also be specified, else an error                           */

  if (fl & VMAC_PACKSHR)
     vmAssert (fl & VMAC_ARENASHR);

  if (fl & VMAC_PROT)
     vmAssert (!(fl & VMAC_PACKSHR));

  /*
   *  See if it is a heap allocation. If so, validate flags, and choose
   *    the appropriate heap.
   */

  if((fl & VMAC_ARENAMASK) == VMAC_ARENAHEAP)
    {
      vmAssert(((fl & VMAC_LOCMASK) == VMAC_LOCANY)  &&
               (((fl & VMAC_ALIGNMASK) == NULL)  ||
               ((fl & VMAC_ALIGNMASK) == VMAC_ALIGNPAGE)) &&
                 ((fl & vmac_INVALID_HEAP) == NULL)  &&
                 ((fl & PG_DISCARDABLE) == 0) &&
                 (hobPTDA == HOBNULL)   &&
                 (blkno == NULL));
      hkh = ((fl & PG_RESIDENT) == 0) ?
                ((fl & PG_W) ? VM_HKH_PUB_SWAPRW : VM_HKH_PUB_SWAPRO) :
                ((fl & PG_W) ? VM_HKH_PUB_RESRW : VM_HKH_PUB_RESRO);

      fl &= (VMAC_ALIGNPAGE | VMAC_NOXPAGE | PG_U | PG_CONTIG |
                VMAC_SELALLOC | PG_1M | PG_16M);

      if(fl & VMAC_SELALLOC)
        {
          fl &= ~VMAC_SELALLOC;
// DCR 32500
          rc = SELAllocKHB(hkh, cb, fl,
                       (flSelMgr & VMACX_SAFLAGS_MSK), hobOwner, pac);
// end
        }
      else
        {
          pac->ac_sel = 0;
          pac->ac_hob = HOBNULL;
          rc = VMAllocKHB(hkh, cb, hobOwner, fl, (char **)(&(pac->ac_va)));
        }
      MMPHPostVMAllocMem();             // Performance Hook
      return(rc);
    }

  /*
   *  Force tiling if it is a shared or private arena, and not a VDM task.
   */

  if((!vmSysArenaReq(fl)) && !(fl & VMAC_VDM))
    {
      fl |= VMAC_SELALLOC;
    }

  /*
   *  Get base virtual address of object (if any).  Set to 0 if a specific
   *  base address has not been specified.
   */

  va = pac->ac_va;
  if(!(vmLocSpecReq(fl) || vmLocAboveReq(fl)))
    {
      va = 0;
    }

  /*
   *  Verify that a non-zero byte count has been specified.
   */

  vmAssert(cb != 0);

  /*
   *  Verify that the requested region lies within the virtual address space
   *  and obtain base page and count of pages for the enclosing page-granular
   *  region.
   */

  if(max(va, cb) >= (MAXSYSPAGE << PAGESHIFT) ||
     (MAXSYSPAGE << PAGESHIFT) - cb < va)
    {
      rc = ERROR_NOT_ENOUGH_MEMORY;
      MMPHPostVMAllocMem();             // Performance Hook (needs rc loaded)
      return(rc);
    }

  ipg = VMRegionBase(va) >> PAGESHIFT;
  cpg = VMRegionSize(va, cb) >> PAGESHIFT;
  cpgCommit = 0;
  if(cbCommit != 0)
    {
      cpgCommit = VMRegionSize(va, cbCommit) >> PAGESHIFT;
    }

  /*
   *  Verify context and owner.
   */

  if(vmSysArenaReq(fl))
    {
      /*
       *  This is a system arena request.  The context is not relevant and
       *  the owner must be one of the special system owners.
       */

      pPTDA = NULL;
      hobPTDA = HOBNULL;
      vmAssert(vmSystemOwner(hobOwner));
    }

  else
    {
      /*
       *  This is a private or shared arena request.  The context is relevant
       *  and a NULL value means that the context is the current context.
       *  Shared and private arena objects must obey the following
       *  ownership rules:
       *
       *  - Instance data objects in the shared arena must be MTE objects
       *    and may only be owned by the target PTDA (HOBNULL means current
       *    PTDA).
       *
       *  - Other MTE objects in the shared or private arena may only be
       *    owned by the specified MTE (which must be non-null).
       *
       *  - API-allocated objects in the shared or private arena may only
       *    be owned by the target PTDA (HOBNULL means current PTDA).
       *
       *  - All other objects are Special System-allocated objects, such as
       *    infosegs, must be owned either by the current task, or by one of
       *    the special system owners.  Where a system-allocated object is
       *    owned by the current task, hobMTE is set to a special system
       *    owner to distinguish the object from MTE objects.
       */

      pPTDA = VMGetPTDAAddr(hobPTDA);
      hobPTDA = pPTDA->ptda_handle;
      if (hobOwner == HOBNULL)
        {
          hobOwner = hobPTDA;
          vmAssert(hobPTDA == pPTDACur->ptda_handle);
        }

#ifdef VMSTRICT

      /*
       *  BUGBUG IDB 10/11/90 - Disable checking for non-API allocations
       *  for which hobMTE = HOBNULL.  There is no mechanism for detecting
       *  a system allocation owned by a task if the allocation was made
       *  via SegAlloc, because that function has no hobMTE parameter.
       */

      if(!((hobMTE == HOBNULL && !(fl & VMAC_OBAPI)) ||
         vmSystemOwner(hobOwner) || vmSystemOwner(hobMTE)))
        {
          /*
           *  The object is not system-allocated, so the object must
           *  either be a loader MTE object or API-allocated.  For loader
           *  objects, hobMTE must be the handle of the MTE to which the
           *  object belongs; for API-allocated objects, hobMTE must
           *  be the handle of the MTE in which the code containing
           *  the API call resides; i.e. the "creating MTE".  Verify
           *  that a valid pseudo-handle has been specified.
           */

          vmAssert(hobMTE != HOBNULL && LDRCheckModHandle(hobMTE) == NO_ERROR);

          if(!(fl & VMAC_OBAPI))
            {
              /*
               *  This is a loader MTE object.  For shared content
               *  (shared or private arena), hobOwner must be the MTE to
               *  which the object belongs.  For private content
               *  (shared or private arena), hobOwner must be the
               *  current task's PTDA handle.
               */

              if(vmShrDataReq(fl))
                {
                  vmAssert(hobOwner == hobMTE);
                }
              else
                {
                  vmAssert(hobOwner == hobPTDA);
                }
            }

          else
            {
              /*
               *  This is an API-allocated object.  The owner must
               *  be the PTDA handle for the creating task.
               */

              vmAssert(hobOwner == hobPTDA);
            }
        }
#endif
    }

  /*
   *  A fully initialized VDM task may only allocate objects in its
   *  own context.
   */

  vmAssert((!(vmVDMTask(pPTDACur))) || vmCurrTask(pPTDA));

  /*
   *  Allocate an object record.  Mark the record as allocated by
   *  setting its owner field.
   */

  if((rc = vmObAlloc(SSToDS(&pobNonReg))) != NO_ERROR)
    {
      MMPHPostVMAllocMem();             // Performance Hook
      return(rc);
    }
  pob = pobNonReg;
  pob->ob_hobowner = hobOwner;
  pob->ob_xflags = 0;                   // Initialize extra flags field
  pac->ac_hob = vmPobToHob(pob);

  /*
   *  Reserve the virtual address space, searching the specified arena
   *  if necessary.  If successful, commit the amount of physical
   *  storage specified.
   */

  flLocal = 0;
  if((rc = VMReserve(cpg,fl,pPTDA,ipg,hobOwner,SSToDS(&par))) == NO_ERROR)
    {
      /*
       *  Get the base page assigned to the object.
       */

      ipg = par->ar_ipg;
      cpg = par->ar_cpg;

      /*
       *  If we are to commit a portion of physical storage from the
       *  beginning of the object, call the Page Manager.
       */

      if(cpgCommit != 0)
        {
// DCR 32500
           if (!(flSelMgr & VMACX_USEHIGHMEM))
           {
               rc = PGAlloc(ipg,
                            pPTDA,
                            cpgCommit,
                            blkno,
                            pac->ac_hob,
                            0,
                            (fl & VMAC_PAFLAGS) );
            } else {
               vmAssert((PGGlobalFlags & PG_HIGH_MEM_INITED)==0);
               rc = PGAllocHighMem(ipg,
                           pPTDA,
                           cpgCommit,
                           blkno,
                           pac->ac_hob,
                           0,
                           (fl & VMAC_PAFLAGS) );
               pob->ob_xflags |= VMOB_HIGHMEM;   // assume success
                                          // the hob will be deleted if error
            }
// end
            if (rc == NO_ERROR)  flLocal |= VMS_COMMIT;
        }

      if(rc == NO_ERROR)
        {
          /*
           *  If selectors were requested, call the Selector Manager
           *  to allocate them.  If error, free the virtual address
           *  space.  Set the selector's descriptor limit field equal to
           *  the unrounded number of bytes being committed - 1, except
           *  for pre-committed objects.  For these, set the limit equal
           *  to the number of bytes being reserved - 1.
           */

          if(fl & VMAC_SELALLOC)
            {
              if(((vmInitFlags & VMS_COMMIT) == 0) ||
                 (cbCommit == 0 && !VMTiledPg(ipg)))
                {
                  /*
                   *  We need to set cbCommit equal to cb if cbCommit is 0,
                   *  and the allocation is in the system arena.
                   *  For system arena reserve only allocations, we set up
                   *  the descriptor as covering the entire reserved area
                   *  and not change it during VMSetMem requests.
                   *  This enables us to return a valid GDT selector back
                   *  for such requests.
                   */

                  cbCommit = cb;
                }

              /*
               *  If a discardable segment allocation, add the
               *  discardable segment RMP record, and strip off
               *  SELAL_DISCSEG since SELAlloc does not use it.
               *  Mark the object record as belonging to a
               *  discardable segment.
               */
              if(flSelMgr & SELAL_DISCSEG) {
                 if((rc = SELAddDiscRec(pac->ac_hob)) == NO_ERROR) { //AZ10331
                    flSelMgr &= ~SELAL_DISCSEG;
                    pob->ob_xflags |= VMOB_DISC_SEG;
                 }
              }

              if (rc == NO_ERROR) {                                  //AZ10331
                 if ((rc = SELAlloc(pob, par, cbCommit, fl,
// dcr 32500
                    (flSelMgr & VMACX_SAFLAGS_MSK),
// end
                    pPTDA, &(pac->ac_sel))) == NO_ERROR)
                       flLocal |= VMAC_SELALLOC;
              }

            }

          if(rc == NO_ERROR)
            {
              /*
               *  Reservation of virtual address space and any commit of
               *  physical storage needed or allocation of selectors has
               *  been successful. Set up the object and the arena
               *  record, and finally link them.
               */

              pob->ob_har = vmParToHar(par);

              /*
               *  The following line stores the permission flags, huge,
               *  shrinkable, and DHSetMem flags in the object record
               */

              pob->ob_fs = (ushort_t)(((fl & VMAC_OBMASK) >> VMAC_OBSHIFT) |
                ((((fl & (PG_RESIDENT|PG_UVIRT)) ? (fl & ~PG_16M) : fl ) <<
                OB_PERMSHFT) & OB_PERMMASK));

              /*
               *  If this is an API allocation, set the API bit in the
               *  object record.
               */
              if (fl & VMAC_OBAPI)
                {
                  pob->ob_fs |= OB_API;
                }

              /*
               *  If shared data requested in the shared or private arena
               *  set shared flag in object record.
               */

              if(vmShrDataReq(fl) && !vmSysArenaReq(fl))
                {
                  pob->ob_fs |= OB_SHARED;
                }

              /*
               *  If the object memory type is resident, the state
               *  is either resident or uvirt.  Record the state.
               */

              if(fl & PG_UVIRT)
                {
                  pob->ob_fs |= OB_UVIRT | OB_RESIDENT;
                }
              else if(fl & PG_RESIDENT)
                {
                  pob->ob_fs |= OB_RESIDENT;
                }

              /*
               *  Set zero fill and physical memory below 1 Meg flags
               *  in object record if appropriate.
               */

              if(fl & PG_ZEROFILL)
                {
                  pob->ob_fs |= OB_ZEROINIT;
                }
              if(fl & PG_1M)
                {
                  pob->ob_fs |= OB_LOWMEM;
                }

              /*
               *  Initialize remaining fields in object record, link to
               *  arena record, fill in return structure and return success
               *  to caller.
               */

              pob->ob_hobowner = hobOwner;
              pob->ob_hobmte = hobMTE;
              pob->ob_wsemowner = 0;
              pob->ob_bsemcnt = 0;
              pob->ob_cllock = 0;
              pob->ob_cslock = 0;

              /* Do the linking in resident code */

              vmLinkOb(pob, par);

              pac->ac_va = ipg << PAGESHIFT;
              rc = NO_ERROR;
              MMPHPostVMAllocMem();     // Performance Hook (needs rc loaded)
              return(rc);
            }
        }

      /*
       *  PGAlloc or SELAlloc failed.  Free up the physical memory
       *  if allocated by this VMAllocMem call.
       */

      if(flLocal & VMS_COMMIT)
        {
          PGFree(ipg, pPTDA, cpgCommit, vmfm_LAST_LDR_CTXT);
        }

      /*
       *  Free up the virtual address space if not in use by another
       *  context.
       */

      if(par->ar_hob == HOBNULL)
        {
          VMVirtFree(par, vmGetArenaHeader(ipg, pPTDA));
        }
    }

  /*
   *  VMReserve failed.  Free the object record created prior to
   *  the VMReserve call.
   */

  vmObFree(pob);
  MMPHPostVMAllocMem();                 // Performance Hook
  return(rc);
}

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

/***EP  VMReserveMem - reserve virtual memory
 *
 *      This function is used to allocate virtual address space only.  No
 *      object is created.  Selectors can optionally be allocated.
 *      WARNING: This function is in swappable code.
 *
 *      VMReserveMem (cb, fl, hobptda, flselmgr, pac)
 *
 *      ENTRY   ulong_t cb              - Size of virtual memory in
 *                                        bytes.  This may not be changed
 *                                        subsequently.  Note that the size
 *                                        will be rounded up to page
 *                                        granularity, by adding the offset of
 *                                        the specific base address (if any)
 *                                        and rounding the result up to a
 *                                        multiple of the page size (4K).
 *              ulong_t fl              - Allocation flags (see notes under
 *                                        VMAllocMem routine below)
 *              VMHOB hobptda           - Target context's PTDA pseudo-
 *                                        handle.  This is relevant for
 *                                        private and shared arena allocations
 *                                        only; for those arenas the value
 *                                        HOBNULL may be specified to mean
 *                                        that the target is the current
 *                                        context.
 *              ulong_t flselmgr        - Selector Manager allocation flags
 *              VMAC *pac               - Address of VMReserveMem output buffer.
 *                                        This is same as a VMAllocMem buff.
 *                                        Parameters that can be written to by
 *                                        VMAllocMem appear in this buffer.
 *                                        Note that in some cases, (ac_va) and
 *                                        (ac_sel) values placed in the buffer
 */

int                     VMReserveMem(cb, fl, hobptda, flselmgr, pac)
ulong_t                 cb;             /* Virtual size to reserve in bytes */
ulong_t                 fl;             /* Allocation flags */
VMHOB                   hobptda;        /* Target PTDA pseudo-handle */
ulong_t                 flselmgr;       /* Selector Manager flags */
register VMAC           *pac;           /* Read/write parameter buffer */
{
    int                 rc;
    ulong_t             ipgreq;
    ulong_t             cpgreq;
    register ptda_t     *pptda;
    VMAR                *par;

    /*
     *  Assert that we cannot pre-reserve an API allocated object.
     */
    vmAssert(!(((fl & VMAC_PRERES) != 0) && ((fl & VMAC_OBAPI) != 0)));

    /*
     *  Calculate page-granular size of request
     */

    if(!vmLocSpecReq(fl))
      {
        pac->ac_va = 0;
      }

    cpgreq = VMRegionSize(pac->ac_va, cb) >> PAGESHIFT;
    ipgreq = VMRegionBase(pac->ac_va) >> PAGESHIFT;
    pptda = VMGetPTDAAddr(hobptda);             /* Map PTDA hdl to addr */

    /*
     *  Reserve the virtual memory
     */

    if((rc = VMReserve(cpgreq, fl, pptda, ipgreq, HOBNULL, SSToDS(&par)))
      == NO_ERROR)
      {
        /*
         *  Reserve successful.  Return the virtual address.  If selectors
         *  were requested, call the Selector Manager to allocate them.
         */

        pac->ac_va = par->ar_ipg << PAGESHIFT;

        if(fl & VMAC_SELALLOC)
          {
            if((rc = SELAlloc(NA,par,cb,fl,flselmgr,pptda,
                       &(pac->ac_sel))) != NO_ERROR)
              {
                /*
                 *  Request to allocate selectors was unsuccessful.  Free
                 *  the virtual memory.
                 */

                VMVirtFree(par,NULL);
              }
          }
        else
          {
            /*
             *  Selectors not requested.  If a tiled pre-reservation request,
             *  calculate and return selector using the Compatibility Region
             *  Mapping Algorithm anyway.  This is necessary because LDT
             *  selectors cannot be requested on a pre-reservation request.
             */

            if(VMTiled(pac->ac_va) && vmPreResReq(fl))
              {
                pac->ac_sel = LaToSelTiled(pac->ac_va);
              }
          }
      }
    return(rc);
}
