/*static char *SCCSID = "@(#)vmshared.c 6.4 92/01/16";*/
#define SCCSID  "@(#)vmshared.c 6.4 92/01/16"

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


/*      This file contains VMM support for shared memory objects. It handles
 *      attachment and detachment of processes from shared objects, and
 *      other concerns relating to shared memory
 */


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

#define INCL_DOSP
#include <os2p.h>


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

#include                <mi.h>
#include                <pgexport.h>
#include                <vmexport.h>
#include                <vmlocal.h>
#include                <selxport.h>
#include                <tkexport.h>
#include                <dossymc.h>         /* tkptda.h needs it */
#include                <sigdef.h>          /* tkptda.h needs it */
#include                <except.h>          /* tkptda.h needs it */
#include                <infoseg.h>         /* tkptda.h needs it */
#include                <curdir.h>          /* tkptda.h needs it */
#include                <dirent.h>          /* tkptda.h needs it */
#include                <find.h>            /* tkptda.h needs it */
#include                <screen.h>          /* tkptda.h needs it */
#include                <sem.h>             /* tkptda.h needs it */
#include                <tkptda.h>
#include                <mmph.h>
#include                <vmowner.h>
#define FOR_EXEHDR      1 /*147968 */
#include <exe386.h>       /*147968 */
#include <ldrxport.h>     /*147968 */
#include <ldrvars.h>      /*147968 */


/***LP  vmLinkAr - Link given arena record to object record's link list.
 *
 *      vmLinkAr links the given arena record to object's link list. It
 *      is used by swappable code which must do link updates in an atomic
 *      operation, without blocking. The fields of arena record must already
 *      filled with valid values.
 *
 *      WARNING: This procedure must be in resident code. The object semaphore
 *      must be obtained when calling this procedure
 *
 *      ENTRY   par     -   Pointer to arena record to be linked
 *              pob     -   Pointer to object record
 *      RETURN  NONE
 *
 *      void    vmLinkAr(par, pob)
 */
void    vmLinkAr(par, pob)
VMAR    *par;
VMOB    *pob;
{
        vmSemAssertOb(pob);
        par->ar_harlink = pob->ob_har;
        pob->ob_har = vmParToHar(par);
        if (vmAliasAr(par)) {
            pob->ob_fs |=OB_LALIAS;
        }

}


/***LP  vmLinkCo   - Link given context record to arena record's link list.
 *
 *      vmLinkCo links given context record to arena record's link list. It
 *      is used by swappable code that must be doing the linking in an
 *      atomic operation (without blocking). The fields of context record
 *      must be filled with valid values, and the object semaphore must
 *      be obtained.
 *
 *      WARNING: This procedure must be in resident code
 *
 *      ENTRY   pco -   Pointer to context record
 *              par -   Pointer to arena record
 *      RETURN  NONE
 *
 *      void  vmLinkCo(pco, par)
 */
void    vmLinkCo(pco, par)
register VMCO    *pco;
register VMAR    *par;
{
    register VMHCO hco;

    vmAssert(par->ar_f & AR_HCO);

//  vmSemAssertOb(vmHandleMap(par->ar_hob));
//  pco->co_hconext = par->ar_hco;
//  par->ar_hco = vmPcoToHco(pco);

    hco = VMHcoAr(*par);
    VMCoHconext(*pco, hco);
    VMArHco(*par, vmPcoToHco(pco));
}


/***LP  vmFindArCo - Walks linked arena and possibly context records belonging
 *                      to an object until one with given owner and
 *                      possibly address is found
 *
 *      vmFindArCo walks the arena records until it matches the context
 *      given or finds one with the context list. If a linear address
 *      is given it also checks to see if given AR includes that given
 *      address. If an AR with context list is matched, it also
 *      walks the context list to find the matching context record.
 *      If AR with context list is requested, it returns the pointer to
 *      that AR regardless of whether corresponding context is found or not.
 *      If corresponding context record isn't found, pco will be zero.
 *      If requested, it skips matching alias records.
 *
 *      This function must be in resident code. It walks the linked
 *      records without taking the object semaphore, and it must not block
 *      in the middle of walking the linked records. (Links may change if it
 *      blocks). It is also called from VMEachMap which must remain in resident
 *      code until Page Fault handling code stops calling it.
 *
 *      ENTRY   fl  - flags vmFAC_NOALIAS: ignore alias records
 *                          vmFAC_NOIPG:match only the hobptda, no ipg match
 *                          vmFAC_HCO: return AR with context list
 *              hobptda -   context to match
 *              ipg     -   page address to match(not used if vmFAC_HCO)
 *              pob     -   Pointer to object record to walk AR list of
 *              ppar    -   Location to return matching par
 *              pparprev -  Location to return back link to par
 *                              (0 if par is first in the list)
 *              ppco    -   Location to return matching pco (=0 if no co list)
 *
 *      int  vmFindArCo(ulong_t fl, VMHOB hobptda, ulong_t ipg, VMOB *pob,
 *              VMAR **ppar, VMAR **pparprev, VMCO **ppco)
 *
 *      PseudoCode
 *
 *      Set har= pob->ob_har    ; get arena record handle
 *      Set *ppar = *pparprev = 0
 *      Set *ppco = 0
 *      While (har != HARNULL)
 *          Convert har to pointer to arena record(par)
 *          If (par->ar_f & AR_HCO)         ; found one with context list?
 *              If (!(fl & vmFAC_NOIPG) ||
 *                  par->ar_ipg <= ipg < par->ar_ipg+ par->ar_cpg)
 *                  ; DO we match the address if needed?
 *                  Set hco = par->ar_hco    ; get first context record index
 *                  While (hco != HCONULL) {
 *                      Convert hco to pointer to context record (pco)
 *                      If (pco->co_hobptda == hptda)   ; found the one we want?
 *                          write pco
 *                          return (NO_ERROR)
 *                      Endif
 *                      Set hco = pco->co_hconext   ; get next record
 *                  }
 *                  Set *ppco = 0               ; no match in context list
 *              Endif                           ; no match with context list
 *              If (fl & vmFAC_HCO) return(NO_ERROR)
 *                  ; AR with HCO found, but co or ipg didn't match
 *          Else if (not an alias or not vmFAC_NOALIAS)
 *              If (par->ar_hobptda == hptda)
 *                  if (vmFAC_NOIPG ||
 *                      par->ar_ipg <= ipg < par->ar_ipg+par->ar_cpg)
 *                      Set *ppar = par
 *                      return (NO_ERROR)
 *                  Endif
 *              Endif
 *          Endif
 *          Set *pparprev = par                 ; Save this arena as backlink
 *          Set har = par->ar_harlink           ; get next arena record index
 *      }
 *      return (ERROR_NOT_FOUND)
 *
 */
int     vmFindArCo(fl, hobptda, ipg, pob, ppar, pparprev, ppco)
ulong_t fl;                     /* Match flags */
VMHOB   hobptda;                /* context to match */
ulong_t ipg;                    /* address to match if !vmFAC_NOIPG */
VMOB    *pob;                   /* Object record containing head of AR list */
VMAR    **ppar;                 /* Location to return par */
VMAR    **pparprev;             /* Location to return back link to par */
VMCO    **ppco;                 /* Location to return pco */
{
    register VMHAR   har;       /* AR handle */
    register VMAR   *par;
    VMHCO   hco;                /* CO handle */

    TKBeginNoBlock();
    if (hobptda == HOBNULL) hobptda = pPTDACur->ptda_handle;
    *pparprev = NULL;           /* initialize pointers */
    *ppco = NULL;
    har = pob->ob_har;
    while (har) {
        par=vmArMap(har);         /* get pointer to AR */
        if ((par)->ar_f & AR_HCO) {   /* context list? */
            /* have to match the address ? */
            if ((fl & vmFAC_NOIPG) || vmWithinArPg(ipg, par)) {
                register VMCO  *pco;
                *ppar = par;                    /* set up return parameter */
//              hco = (par)->ar_hco;          /* get first context record */
                hco = VMHcoAr(*par);          /* get first context record */
                while (hco != HCONULL)
                {
                    pco = vmCoMap(hco);   /* get address of context record */
                    if ((pco)->co_hobptda == hobptda) {
                        *ppco = pco;
                        TKEndNoBlock();
                        return (NO_ERROR);
                    }
//                  hco = (pco)->co_hconext;    /* get next record */
                    hco = VMHconextCo(*pco);    /* get next record */
                }
                *ppco = NULL;           /* Context didn't match */
            }
            /* No context match */

            /* Here we found the context list AR, but the context or ipg
             * didn't match. If AR with co list requested, we return
             * with no error.
             */
            if (fl & vmFAC_HCO) {
                TKEndNoBlock();
                return(NO_ERROR);
            }
        }                   /* end AR_HCO block */
        else
        {
            if(!vmAliasAr(par) || !(fl & vmFAC_NOALIAS))
            {
                /* skip if alias & so requested */
                if((par->ar_hobptda == hobptda) ||
                   ( !(fl & vmFAC_NOIPG) && VMSysArenaPg(ipg)) ||
                   (VMShrArenaPg(par->ar_ipg) && vmPrvOb(pob)))
                {
                    if((fl & vmFAC_NOIPG) || vmWithinArPg(ipg, par))
                    {
                        *ppar = par;
                        TKEndNoBlock();
                        return (NO_ERROR);
                    }
                };
            };
        }
        *pparprev = par;                /* update backlink */
        har= (par)->ar_harlink;         /* get next AR handle */
    }
    TKEndNoBlock();
    return (ERROR_INVALID_DATA);  /* BUGBUG:ERROR_NOT_FOUND would be better */
}


/***EP  VMSearchRef - Search references for a given shared memory object
 *
 *      VMSearchRef searches references for a given shared memory object.
 *      It only searches for context list and shared arena records,
 *      without counting aliases. It odesn't count privatized contexts.
 *
 *      This function walks the linked Arena and object records without
 *      taking the object semaphore. It must not block in the middle of
 *      walking these records, therefore it must be in resident code.
 *
 *      ENTRY   flags - Action flags
 *                      1 = VMSR_ONE  Search for one reference
 *                      2 = VMSR_TWO Search for two or more references
 *              hob   - handle to object record
 *      RETURN  int     - error code (NO_ERROR if asked no of references exist)
 *
 *      VMSearchRef (ulong flags, VMHOB hob)
 *
 *      PseudoCode:
 *
 *      Call vmHandleMap (hob)
 *      If (not HF_SHARED), InternalError
 *      Set num = 0
 *      Set har = pob->ob_har           ; get arena record index
 *      While (har != HARNULL) {
 *          Convert har to pointer to arena record(par)
 *          If (par->ar_f && AR_HCO)         ; found one with context list?
 *              Set hco = par->ar_hco
 *              while (hco != HCONULL)
 *                  Convert hco to pointer to context record(pco)
 *                  If (not privatized context)
 *                      Increment num
 *                      If (num >= SR flags) return (NO_ERROR)
 *                  Endif
 *                  Get next context record in the list
 *              return (ERROR_NOT_FOUND)
 *          Else if (not an alias)
 *              Increment num
 *              If (num >= SR flags) return (NO_ERROR)
 *          Endif
 *          Set har = par->ar_harlink       ; get next arena record index
 *      }
 *      return (ERROR_NOT_FOUND)
 */
int            VMSearchRef (fs, hob)
ulong_t        fs;                 /* search for one/two references */
VMHOB          hob;                /* Handle to object */
{
    VMOB     *pob;                  /* pointer to object record */
    VMCO     *pco;                  /* pointer to context record */
    register VMHCO hco;             /* Handle to context record */
    register VMHAR har;             /* handle to arena record */
    register VMAR *par;             /* pointer to arena record */
    ulong_t cref=0;                 /* no of references found */

    TKBeginNoBlock();
    pob= vmHandleMap(hob);          /* Get object record's handle */
    vmAssert(vmShrOb(pob)); /* Internal Error if not shared */
    har=pob->ob_har;                /* get handle to AR */
    while (har != HARNULL)
    {
        par=vmArMap(har);           /* get pointer to AR */
        if (par->ar_f & AR_HCO)     /* found one with context list ? */
        {
//          hco = par->ar_hco;      /* get handle to context record */
            hco = VMHcoAr(*par);    /* get handle to context record */
            while (hco != HCONULL)
            {
                pco = vmCoMap(hco);
                if (!vmPrivCo(pco))         /* skip privatized ones */
                {
                    if (++cref >= fs)
                    {
                        TKEndNoBlock();
                        return(NO_ERROR);
                    }
                }
//              hco = pco->co_hconext;
                hco = VMHconextCo(*pco);
            }
            TKEndNoBlock();
            return (ERROR_INVALID_DATA); /*BUGBUG: ERROR_NOT_FOUND */
        }
        else
        {
            if (!vmAliasAr(par) && !vmPrivAr(par))
                    /* ignore aliases and privatized contexts */
            {
                if (++cref >= fs)             /* found desired no of refs? */
                {
                    TKEndNoBlock();
                    return (NO_ERROR);
                }
            }
        }
        har = par->ar_harlink;              /* get next AR */
    }
    TKEndNoBlock();
    return (ERROR_INVALID_DATA);            /* BUGBUG: ERROR_NOT_FOUND */
}


/***EP  VMIsAttached - See if given object is attached to a process.
 *
 *      VMIsAttached returns no error if given context is attached to
 *      the memory object
 *
 *
 *      ENTRY   hob     - handle to the memory object record
 *              hptda   - handle to ptda
 *      RETURN  int     - error code (NO_ERROR if context is attached)
 *
 *      VMIsAttached (VMHOB hob, VMHOB hptda)
 *
 *      PseudoCode:
 *
 *      Call vmHandleMap
 *      Return(vmFindArCo(skip aliases, no ipg match)
 */
int            VMIsAttached (hob, hptda)
VMHOB          hob;                /* handle to object record */
VMHOB          hptda;              /* handle to context       */
{
    VMAR    *par;                   /* passed to vmFindArCo */
    VMAR    *par2;                  /* passed to vmFindArCo */
    VMCO    *pco;                   /* passed to vmFindArCo */

    return(vmFindArCo(vmFAC_NOIPG | vmFAC_NOALIAS, hptda, NULL,
        vmHandleMap(hob), SSToDS(&par), SSToDS(&par2), SSToDS(&pco)));

}


/***LP  vmEachMapCheck - Check to see if worker needs to be applied to given
 *              Arena Record address
 *
 *      vmEachMapCheck returns nonzero value if the worker needs to be applied
 *      to the given context.
 *
 *      ENTRY   fl  -   flag passed to VMEachMap
 *              pal -   Alias Record (needed only if an alias)
 *              hptda - ptda to match if privatization
 *      RETURN  int -   Nonzero value if the worker is to be called
 *
 *      int vmEachMapCheck(fl, far, fal)
 *
 *      PseudoCode:
 *
 *      if (AR is not an alias) return (fl & VMEM_CONTEXT)
 *      else                ; is an alias
 *          assert (VMEM_DBGALIAS == AL_DBGALIAS)
 *          assert (VMEM_SELALIAS == AL_CSALIAS | AL_MEMMAP)
 *          if ((fl & (VMEM_DGBALIAS | VMEM_SELALIAS)) & pal->al_f)
 *              return(TRUE)
 *          else
 *              return (fl & VMEM_VMALIAS)
 *          endif
 *      endif
 */

int
vmEachMapCheck(ulong_t fl,              /* flags passed to VMEachMap */
               register VMAL *pal)      /* Alias record, 0 if not an alias */
{
    if (pal == NULL)
    {
        return(fl & VMEM_CONTEXT);      /* Not an alias */
    }

    /*
     *  AR is an alias
     */

    vmAssert((VMEM_DBGALIAS == AL_DBGALIAS) &&
             (VMEM_SELALIAS == (AL_CSALIAS | AL_MEMMAP)));

    if (fl & (VMEM_DBGALIAS | VMEM_SELALIAS) & pal->al_f)
    {
        return(TRUE);           /* Worker needs to be applied */
    }

    /*
     * We come here only if AR is an alias but not SEL or DBG alias
    */

    return(fl & VMEM_VMALIAS);
}


/***LP  vmEachMapSub - Walk AR chain, calling worker function if needed.
 *
 *      vmEachMapSub walks the AR chain linked to object record, correcting
 *      offset and page count values and calling the given worker if
 *      needed. It decrements map count each time worker is called successfully,
 *      and stops when map count reaches zero or end of AR chain is reached.
 *
 *      ENTRY   pfworker    - pointer to worker function
 *              pdata       - pointer to data passed to worker
 *              cpg         - no. of pages to search for map
 *              dpg         - displacement from start of object
 *              fl          - VMEachMap flags
 *              pob         - pointer to object record
 *              pba         - pointer to base alias record (only if OB_LALIAS)
 *              pcmap       - pointer to map count parameter
 *      RETURN  int         - error code
 *
 *  int vmEachMapSub(void *pfworker, void *pdata, ulong_t cpg, ulong_t dpg,
 *      ulong_t fl, VMOB *pob, VMAL *pba, ushort_t *pcmap)
 *
 *      PseudoCode:
 *
 *      For every arena index in arena link list
 *          Convert arena index to arena pointer
 *          if (an alias) find pointer to alias record
 *          If (vmEachMapCheck returned nonzero)  ; have to call worker
 *              ; Set up parameters for the worker function
 *              ; ipgw = page address passed to worker
 *              ; pgoffw = offset passed to worker
 *              ; cpgw = No of pages passed to worker
 *              ; ipg, cpg = passed linear address and no of pages
 *              Set ipgw = ar_ipg + dpg ; address is different for aliases
 *              pgoffw = 0
 *              cpgw = cpg
 *              If (an alias)       ; arena record is an alias
 *                  ; correct ipgw, cpgw, pgoffw values
 *                  If (not (SELMGR alias))         ; Regular alias
 *                      Find the true size of the object
 *                      If(al_pgoff <= dpg)
 *                          Set ipgw = ar_ipg + dpg - al_pgoff
 *                          Set pgoffw = 0
 *                          Call SELGetSize(ar_ipg << PAGESHIFT, ar_hobptda)
 *                          If (size - dpg + al_pgoff <= cpg)
 *                              Set cpgw = size - dpg + al_pgoff
 *                          Else
 *                              Set cpgw = cpg
 *                          Endif
 *                      Else                ; dpg < al_pgoff
 *                          If (al_pgoff >= dpg+cpg) go to next AR
 *                                          ; alias outside the range
 *                          Set ipgw = ar_ipg
 *                          Set pgoffw = al_pgoff - dpg
 *                          Call SELGetSize(ar_ipg, ar_hptda)
 *                          If (size + pgoffw >= cpg)
 *                              Set cpgw = cpg - pgoffw
 *                          Else            ; size + pgoffw < cpg
 *                              Set cpgw = size - pgoffw
 *                          Endif
 *                      Endif
 *                  Else if (cs alias && passed address in datasel)
 *                      if cpg > 16    ; size greater than 64K?
 *                          set cpgw = 64K
 *                      endif
 *                  Endif
 *              Endif                   ; end if alias block
 *              If (ar_flags && AR_HCO) ; has a context list
 *                  For every context on context list
 *                      Call *pfworker(pdata, ipgw, cpgw, hptda, pgoffw)
 *                      If (error from worker and VMEM_ERRSTOP), return(error)
 *                      Else
 *                          decrement cmap
 *                          if (cmap == 0), return NO_ERROR
 *                      Endif
 *              Else
 *                  Call *pfworker(pdata, ipgw, cpgw, ar_hptda, pgoffw)
 *                  If (error from worker and EM_ERRSTOP), return(error)
 *                  Else
 *                      decrement cmap
 *                      if (cmap == 0), return NO_ERROR
 *                  Endif
 *              Endif
 *          Endif
 *          set har = par->ar_harlink       ; get next AR
 *      return(NO_ERROR)                    ; End of AR chain reached
 */

typedef int (*pfnEachMap)(void *pdata,  /* pointer to worker function */
                          ulong_t ipgw,
                          ulong_t cpgw,
                          VMOD *pod,
                          ulong_t pgoffw);

int
vmEachMapSub(pfnEachMap pfworker,   /* pointer to worker function        */
             void     *pdata,       /* pointer to data area              */
             ulong_t  cpg,          /* count of pages to search for map  */
             ulong_t  dpg,          /* displacement from start of object */
             ulong_t  fl,           /* VMEachMap flags                   */
             VMOB     *pob,         /* Pointer to object record          */
             VMHOB    hptda,        /* Context information               */
             ushort_t *pcmap)       /* Counter                           */
{
    int           rc;               /* return code                       */
    VMHAR         har;              /* handle to arena record            */
    register VMAR *par;             /* pointer to arena record           */
    register VMAL *pal;             /* pointer to alias record           */
    VMHCO         hco;              /* handle to Context Record          */
    ulong_t       ipgw;             /* page address passed to worker     */
    ulong_t       pgoffw;           /* page offset passed to worker      */
    ulong_t       cpgw;             /* page offset passed to worker      */
    ulong_t       cbtrue;           /* true size if alias                */
    ulong_t       cpgal;            /* alias page count                  */
    VMHOB         hPTDA;            /* PTDA handle                       */
    VMOD          od;               /* object descriptor structure       */
    register VMOD *pod;             /* pointer to object descriptor      */

    pod = SSToDS(&od);
    pod->od_pob = pob;
    har         = pob->ob_har;      /* get first AR on the list */

    while (har != HARNULL)
    {
        par = vmArMap(har);
        if (vmAliasAr(par))
        {
            pal = vmHalToPal(par->ar_hal);
        }
        else
        {
            pal = NULL;
        }

        pod->od_par = par;

        if (VMBaseSelMapAllOd(od) || vmEachMapCheck(fl, pal))
        {
            /*
             * worker needs to be called
             */

            pod->od_laddr = par->ar_ipg << PAGESHIFT;

            ipgw   = par->ar_ipg + dpg; /* alias addresses may be different */
            pgoffw = 0;
            cpgw   = cpg;

            if (pal != NULL)            /* Is an alias */
            {
                if (!(vmIsSELAlias(pal)))
                {
                    /*
                     * Find the true size of the object
                     */
                    if (par->ar_f & AR_SELMAP)
                    {
                        rc = SELGetSize(pod->od_laddr, par->ar_sel,
                                            par->ar_hobptda,
                                            par->ar_cpg << PAGESHIFT,
                                            SSToDS(&cbtrue));
                        vmAssert(rc == NO_ERROR);
                    }
                    else
                    {
                        cbtrue = par->ar_cpg << PAGESHIFT;
                    }

                    if (pal->al_pgoff <= dpg)
                    {
                        ipgw -= pal->al_pgoff;
                        cpgal = ((cbtrue + PAGEMASK)>>PAGESHIFT)+pal->al_pgoff;

                        if ((cpgal < dpg) ||
                            ((cpgal == dpg) && !VMBaseSelMapAllOd(*pod)))
                        {
                            goto ems80;      /* skip - alias outside range */
                        }

                        cpgal -= dpg;

                        if ((cpgal < cpg) && (cpgal != NULL))
                        {
                            cpgw = cpgal;
                        }
                    }
                    else            /* dpg < al_pgoff */
                    {
//   d# 109193          if (pal->al_pgoff >= (dpg + cpg)) goto ems80;
                        if (pal->al_pgoff >= (dpg + cpg)) // d# 109193
                        {
                            goto ems80;       /* Alias outside the range */
                        }

                        ipgw = par->ar_ipg;
                        pgoffw = pal->al_pgoff - dpg;
                        cpgal = ((cbtrue + PAGEMASK) >> PAGESHIFT) + pgoffw;

                        if (cpgal >= cpg)
                        {
                            cpgw = cpg - pgoffw;
                        }
                        else
                        {
                            cpgw = cpgal - pgoffw;
                        }
                    }
                }                                  /* end not SELMGR alias */
                else        /* Is a SELMGR alias */
                {
                    /*
                     * Is a SELMGR alias
                     */

                    if (pal->al_f & AL_CSALIAS)
                    {
                        if (dpg > (0x10000 >> PAGESHIFT))
                        {
                            goto ems80;     /* skip cs alias if disp > 64Kb */
                        }

                        if (cpg > (0x10000 >> PAGESHIFT)) /* size > 64K */
                        {
                            cpgw = 0x10000 >> PAGESHIFT;  /* Size = 64K */
                        }
                    }
                }
            }                         /* end if alias block */

//          hco = par->ar_hco;
            hco = VMHcoAr(*par);
            do
            {
                if(par->ar_f & AR_HCO)
                {
                    vmAssert(hco != HCONULL);

                    pod->od_pco = vmCoMap(hco);
                    hPTDA = (pod->od_pco)->co_hobptda;
//                  hco = (pod->od_pco)->co_hconext;
                    hco = VMHconextCo(*(pod->od_pco));
                }
                else
                {
                    pod->od_pco = NA;
                    hPTDA = (VMSysArenaPg(ipgw) ? HOBNULL : par->ar_hobptda);
                    hco = HCONULL;
                }
                pod->od_pPTDA = VMGetPTDAAddr(hPTDA);
                pod->od_pah = vmGetArenaHeader(par->ar_ipg, pod->od_pPTDA);

                rc= (*pfworker)(pdata, ipgw, cpgw, pod, pgoffw);

                if ((rc != NO_ERROR) && (fl & VMEM_ERRSTOP))
                {
                    return(rc);
                }
                if (--(*pcmap) == 0)
                {
                    return(NO_ERROR);
                }

            }while(hco != HCONULL);

        }                           /* end EachMapCheck loop */

ems80:
        har = par->ar_harlink;      /* get next AR */
    }                               /* end of AR chain list reached */

    return(NO_ERROR);
}


/***EP  VMEachMap - Apply the worker function to some or all maps to a given
 *                      memory region.
 *
 *      VMEachMap walks the arena records of a memory object and its local
 *      alias records to find all pages mapping to given address range
 *      and if needed calls the worker function.
 *      When the state of a physical page or range of pages is changed,
 *      this function can be called to update all pte's mapping the
 *      same physical area.
 *      This function assumes that the given linear address range lies
 *      within the same memory object.
 *      The worker and the error function passed to VMEachMap should be
 *      of the form:
 *          int fworker(void *pdata, ulong_t ipg, ulong_t cpg, VMOI *poi,
 *              ulong_t pgoff)
 *      where
 *          pdata   is the address of data area passed to this function
 *          ipg     start page address of the map
 *          cpg     size of the map
 *          poi     pointer to partially filled in object information structure
 *                  with the following fields valid:
 *                      oi_hobptda
 *                      oi_hob
 *                      oi_har
 *                      oi_fsob
 *                      oi_fsar
 *                      oi_hco
 *          pgoff   offset of the start of the map from the passed address
 *
 *      WARNING: return code from error worker function is not checked, it is
 *      best if the error function InternalErrors in case of unrecoverable
 *      error.
 *
 *      WARNING: VMEachMap will also iterate over privatized contexts as
 *      well, since privatized contexts may still have shared regions in
 *      them. It is the caller's responsibility to distinguish between
 *      privatized and shared pages.
 *
 *      BUGBUG: This function can be moved to swappable code when Page Fault
 *      handling code stops using it.
 *
 *      ENTRY   pfworker - Address of worker function
 *              pdata    - Address to pass to worker function when called
 *              ipg      - page address of memory region to find aliases to
 *              hptda    - context that given address belongs to
 *              cpg      - size in pages of memory region
 *              fl       - action flags
 *                          VMEM_ERRSTOP - stop if error from worker, call
 *                           error worker (if not null) for every map where
 *                           the worker function succeeded
 *                          VMEM_NOSTOP - ignore error code returned by worker
 *                          VMEM_CONTEXT - Call worker for every attached ptda
 *                              (once if system arena)
 *                          VMEM_SELALIAS - Call worker for SELMGR alias maps
 *                          VMEM_DBGALIAS - Call worker for DEBUG alias maps
 *                          VMEM_VMALIAS - Call worker for non SEL, non DEBUG
 *                              alias maps
 *                          VMEM_ALIAS - Call worker for only aliases
 *                          VMEM_ALL - Call worker for all mappings
 *              perrworker - pointer to error function to call
 *              perrdata   - pointer to error function data area
 *      RETURN  int     - error code (NO_ERROR if no error)
 *
 *      int VMEachMap (int (*pfworker)(), void *pdata, ulong_t ipg, VMHOB hptda,
 *                    ulong_t cpg, ulong_t fl, int (*perrworker)(),
 *                    void *perrdata)
 *
 *      PseudoCode:
 *
 *      Assert((fl & (VMEM_ALL | VMEM_CONTEXT | VMEM_SELALIAS | VMEM_DBGALIAS
 *              | VMEM_VMALIAS)) != NULL)
 *      Call VMGetHandle(ipg >> PAGESHIFT, hptda)
 *      Call VMGetSem(NOBLOCK,TAKESEM)
 *      if (error), return (error code)
 *      Convert handle to pointer to handle data
 *      Find the Arena record corresponding to  passed context and laddr
 *      Set dpg = passed ipg - ar_ipg
 *      If (OB_ALIAS), Call vmSearchAliasRec(handle) ; Find address of
 *      alias recs
 *      If (Arena record is an alias)
 *          Find corresponding local alias record
 *          If (not SELMGR alias)
 *              Set dpg = dpg+al_pgoff ; disp is from base of object address
 *          Endif
 *      Endif
 * ifdef VMSTRICT
 *      If (dpg + cpg > ar_cpg), InternalError
 * endif
 *      Set cmap = -1              ; highest possible value
 *      Call vmEachMapSub(pfworker, pdata, dpg, cpg, fl, pob, pba, &pcmap)
 *      If (error from vmEachMapSub && perrworker != NULL)
 *          negate pcmap
 *          Call vmEachMapSub(perrworker, perrdata, dpg, cpg, fl & not ERRSTOP,
 *              pob, pba, &pcmap)
 *      Endif
 *      Call VMClearSem
 *      return(error code from first sub call)
 */

int VMEachMap(pfworker, pdata, lpg, hobPTDA, cpg, fl, perrworker, perrdata)
int         (*pfworker)(void *pdata,    /* pointer to worker function */
                        ulong_t ipgw,
                        ulong_t cpgw,
                        VMOD *pod,
                        ulong_t pgoffw);
void                    *pdata;         /* Pointer to data area */
ulong_t                 lpg;            /* Linear page address */
VMHOB                   hobPTDA;        /* Handle to ptda */
ulong_t                 cpg;            /* Size in pages of region */
ulong_t                 fl;             /* Action flags */
int         (*perrworker)(void *pdata,  /* Pointer to error worker function */
                          ulong_t ipgw,
                          ulong_t cpgw,
                          VMOD *pod,
                          ulong_t pgoffw);
void        *perrdata;                  /* Pointer to error function data */
{
  register VMAR         *par;           /* Pointer to arena record */
  VMAL                  *pal;           /* Pointer to alias alias record */
  ulong_t               dpg;            /* Offset from start of object */
  VMOD                  od;             /* Object data */
  int                   rc;             /* Result code */
  ushort_t              cmap = -1;      /* Map count */

  /*
   *  Make sure at least one kind of map specified.
   */

  vmAssert(fl & (VMEM_ALL | VMEM_CONTEXT | VMEM_SELALIAS | VMEM_DBGALIAS
        | VMEM_VMALIAS));


  od.od_pPTDA = VMResGetPTDAAddr(hobPTDA);
  od.od_laddr = lpg << PAGESHIFT;

  /*
   *  Get object data for the given address and context.
   */

  if((rc = VMGetOd(VMGS_TAKESEM | VMGS_NOBLOCK, SSToDS(&od))) == NO_ERROR)
    {
      par = od.od_par;
      dpg = lpg - par->ar_ipg;
      vmAssert(dpg + cpg <= par->ar_cpg);

      /*
       *  If an alias arena record, get the alias record.
       */

      if(vmAliasAr(par))
        {
          pal = vmHalToPal(par->ar_hal);
          if(vmIsSELAlias(pal) == 0)  /* function should be boolean */
            {
              dpg += pal->al_pgoff;
              hobPTDA = pal->al_hobptda; /* correct the privatized context */
            }
        }

      /*
       *  Call function to call worker for each mapping.
       */

      if(((rc = vmEachMapSub(pfworker, pdata, cpg, dpg, fl, od.od_pob, hobPTDA,
        SSToDS(&cmap))) != NO_ERROR) && (perrworker != NULL))
        {
          /*
           *  Iterate error function for each context in which worker succeeded.
           */

          cmap = ~cmap;                 /* stop when cmap reaches 0 */
          if(cmap != 0)
            {
              vmEachMapSub(perrworker, perrdata, cpg, dpg, fl & (~VMEM_ERRSTOP),
                od.od_pob, hobPTDA, SSToDS(&cmap));
            }
        }
    }
  else
    {
      /*
       *  BUGBUG IDB 08/23/90 - The following error code translation
       *  is retained from the previous version of VMEachMap.  Is it
       *  needed?
       */

      rc = ERROR_INVALID_PARAMETER;
    }
  VMClearSem(VMHobOd(od));
  return(rc);
}


/***EP  VMScanMaps - Call worker function for every mapping of a single page.
 *
 *      VMScanMaps walks the AR chain linked to the specified object record
 *      and calls the given worker if needed.  The displacement value is
 *      converted to a linear page number for each mapping.  This code does
 *      not block (and must not block).
 *
 *      The displacement parameter is considered to be the true displacement
 *      modulo 256M bytes.  Therefore, every page within the object at the
 *      specified displacement plus a multiple of 64K pages will be examined.
 *
 *      NOTE:  - The handle semaphore is NOT taken.  This code assumes that the
 *               appropriate VM data structures (ob, ar, co) are properly linked
 *               together at all times.  This is done to avoid a semaphore
 *               hierarchy violation.
 *             - The worker function must not block.
 *             - There is no provision for the worker to return error.
 *
 *      ENTRY   pfworker    - pointer to worker function
 *              pdata       - pointer to data passed to worker
 *              dpg         - displacement from start of object, modulo 64K
 *              hob         - handle of object record
 *      RETURN  none
 *
 */

typedef void (*pfnScanMaps)(void *pdata, page_t ipg, struct ptda_s *pptda);

void                    VMScanMaps(pfworker, pdata, dpg, hob)
pfnScanMaps             pfworker;
void                    *pdata;
ushort_t                dpg;
VMHOB                   hob;
{
    VMHAR               har;            /* handle to arena record */
    register VMAR       *par;           /* pointer to arena record */
    VMAL                *pal;           /* pointer to alias record */
    VMHCO               hco;            /* handle to Context Record */
    VMOB                *pob;           /* pointer to object record */
    register VMCO       *pco;           /* pointer to Context Record */
    ulong_t             ipg;            /* page address passed to worker */
    ulong_t             xpg;            /* page index */

    TKBeginNoBlock();
    vmAssert(VMHandleVerify(hob,0,NULL) == NO_ERROR);

    pob = vmHandleMap(hob);             /* get pointer to object record */
    har = pob->ob_har;                  /* get first arena record handle */
    vmAssert(har != HARNULL);

    /*
     * Scan all arena records linked to the object record.
     */
    do {
        par = vmArMap(har);
        xpg = dpg;
        vmAssert(dpg < par->ar_cpg || vmAliasAr(par));

        /*
         * Scan the pages at every 256M within the object.
         */
        do {
            ipg = par->ar_ipg + xpg;

            /*
             * Handle aliases.  Account for the fact that the base of an alias
             * may not map to the base of the object it's aliasing.
             */
            if (vmAliasAr(par)) {
                pal = vmHalToPal(par->ar_hal);

                if (!vmIsSELAlias(pal)) {
                    if (xpg < pal->al_pgoff) {
                        continue;               /* before beginning of alias */
                    }
                    if (xpg >= pal->al_pgoff + par->ar_cpg) {
                        break;                  /* beyond end of alias */
                    }

                    ipg -= pal->al_pgoff;

                } else if ((pal->al_f & AL_CSALIAS) &&
                    xpg >= _64K >> PAGESHIFT) {
                    break;                      /* beyond end of cs alias */
                }
            }

            if (par->ar_f & AR_HCO) {
//              hco = par->ar_hco;
                hco = VMHcoAr(*par);
                vmAssert(hco != HCONULL);

                /*
                 * Scan all context records linked to the arena record, and call
                 * the worker on each one.
                 */
                do {
                    pco = vmCoMap(hco);
                    (*pfworker)(pdata, ipg, VMResGetPTDAAddr(pco->co_hobptda));
//              } while ((hco = pco->co_hconext) != HCONULL);
                } while ((hco = VMHconextCo(*pco)) != HCONULL);

            } else {
                /*
                 * Just a simple arena record.  Call the worker.
                 */
                (*pfworker)(pdata, ipg,
                    VMSysArenaPg(ipg) ? 0 :
                    (VMShrArenaPg(ipg) ?
                    VMResGetPTDAAddr(pob->ob_hobowner) :
                    VMResGetPTDAAddr(par->ar_hobptda)));
            }
        } while ((xpg += _64K) < par->ar_cpg);
                       /* _64K == 256meg / PAGESIZE */
    } while ((har = par->ar_harlink) != HARNULL);

    TKEndNoBlock();
}



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

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


/***EP  VMAttach - Attach a given shared memory object to a process.
 *
 *      VMAttach attaches the shared memory object to a process.
 *      If the context is attached already, VMattach returns NO_ERROR.
 *      Memory object must be shared otherwise VMAttach will Internal Error.
 *      WARNING: It is the caller's responsibility to assure that the
 *      source and the target context is and will remain valid thorughout
 *      this call.
 *
 *      ENTRY   hob     - handle to the memory object record
 *              hptda   - handle to ptda to receive access to shared memory
 *              fl      - page manager access permision flags (PG_*)
 *                        0 means use default permissions of target object
 *      RETURN  int     - error code (NO_ERROR if no error)
 *
 *      VMAttach (VMHOB hob, ulong laddr, VMHOB hptda, ulong fl)
 *
 *      PseudoCode:
 *
 *      Call VMGetSem(TAKESEM,VERIFY)
 *      If (error), return (error code)
 *      Call vmFindArCo(return context list if found, skip aliases)
 *      If (ERROR_NOT_FOUND)
 *          ; Private EXE, there is no context list
 *          Call VMReserve
 *      Else
 *          ; Either we are already attached or must have found context list
 *          If (!(par->ar_f & AR_HCO) || (pco != NULL))
 *              ; We are already attached
 *              Call VMClearSem
 *              return(NO_ERROR)
 *          Endif
 *          Call vmCoAlloc
 *      Endif
 *      If (return code != NO_ERROR)
 *          Call VMClearSem
 *          return
 *      Endif
 *      If (PGAttach == NO_ERROR)
 *          ; attached to object successfully
 *          If ((par->ar_f & AR_SELMAP) && (SELCopy != NO_ERROR))
 *              ; SELCopy failed, fall through error path
 *          Else
 *              ; All succeeded, set up and link records and return
 *              If (Context record allocated)
 *                  Set co_hobptda = hptda
 *                  link context record
 *              Else        ; AR allocated
 *                  Set ar_hob = hob
 *                  Set ar_hobptda = hptda
 *                  Set ar_f = ar_f of linked Arena Record
 *                  Link AR to object's AR link list
 *              Endif
 *              Call VMClearSem(hob)
 *              return(NO_ERROR)
 *          Endif
 *          ; We come here if needed tiling and SELCopy failed because
 *          ; we couldn't grow the LDT.
 *          Call PGFree
 *      Endif
 *      ; We come here if either PGAttach or SELCopy failed
 *      If (Co allocated)
 *          Call vmCoFree
 *      Else
 *          Call vmVirtFree
 *      Endif
 *      Call    VMClearSem(hob)
 *      return (error code)
 */
int            VMAttach (hob, hptda,fl)
VMHOB          hob;                /* handle to object to be attached to */
VMHOB          hptda;              /* context to attach */
ulong_t        fl;                 /* Flags */
{
    struct ptda_s  *pptda = VMGetPTDAAddr(hptda);
    ulong_t     csel;               /* No of sels to copy */
    VMOB        *pob;               /* pointer to object record */
    int         rc;                 /* return code */
    register    VMAR *par;          /* pointer to arena record */
    VMAR        *par1, *par2;       /* pointer to another arena record*/
    VMHCO       hco;                /* Handle to context record */
    register    VMCO *pco;          /* pointer to context record */
    VMCO        *pco1 = 0;          /* pointer to context record */
    VMHOB       hptdasrc;           /* source ptda */
    int         reload = 0;

    TKMayBlock();
    vmAssert((fl & ~(PG_X | PG_U | PG_W | PG_R | PG_GUARD)) == NULL);
    if ((rc = VMGetSem(hob, VMGS_TAKESEM | VMGS_VERIFY,
        SSToDS(&pob)) ) != NO_ERROR) return (rc);

    MMPHPreVMAttach();                          /* Performance Hook (20/17) */

    vmAssert (vmShrOb(pob));    /* InternalError if not shared */
    if (hptda == HOBNULL) hptda = pptda->ptda_handle;
    rc = vmFindArCo(vmFAC_HCO | vmFAC_NOALIAS | vmFAC_NOIPG, hptda, NULL,
            pob, SSToDS(&par1), SSToDS(&par2), SSToDS(&pco1));
    if (rc != NO_ERROR) {
        /* No context record, must be private EXE, will add AR */
        par = vmArMap(pob->ob_har);
        while ((vmAliasAr(par) || vmPrivAr(par)) &&
            (par->ar_harlink != HARNULL)) {
            par = vmArMap(par->ar_harlink);
            /* find nonalias, nonprivatized AR */
        }
        /*
         * VMAttach should never be called for objects with only aliases
         * (case of cs alias).
         */
        vmAssert(par);
        if (vmPrivAr(par))
           reload++;

        hptdasrc = par->ar_hobptda;    /* found the source hptda */
        rc = VMReserve(par->ar_cpg, VMAC_LOCSPECIFIC
        | VMAC_ARENAPRV, pptda, par->ar_ipg, 0, SSToDS(&par1));
        if (rc == NO_ERROR) {
            /* Set up flags, handle and owner fields in AR */
            par2 = par;                 /* change register variables */
            par = par1;                 /* pass to register variable */
            par->ar_f |= par2->ar_f & (AR_PERMISSION | AR_SELMAP);
            par->ar_hob = hob;
            par->ar_hobptda = hptda;
            /*
             * We can safely use default permissions, since loader objects'
             * default permissions never change.
             */
            if (fl == NULL) {
                fl = (par2->ar_f & AR_PERMISSION) >> AR_PERMSHFT;
            };
        };
    }
    else {
        /* Must be context list, or we are already attached to it. */
        par = par1;                     /* pass to register variable */
        if (!(par->ar_f & AR_HCO) || (pco1 != NULL)) {
            /* We are already attached */
            vmAssert((par->ar_f & AR_HCO) ? (pco1->co_hobptda == hptda) :
                (par->ar_hobptda == hptda));

            /* We may have to 'or' some permissions. Check for this. */
            rc = NO_ERROR;
            if (fl != NULL) {
                /* If this is not an old style, loader attach */
                rc = VMSetMem(par->ar_ipg << PAGESHIFT,
                    par->ar_cpg << PAGESHIFT, hptda,
                    fl | PG_SKIPFREE | VMS_INCPERM, NULL);
            }
            VMClearSem(hob);
            MMPHPostVMAttach();                 /* Performance Hook (20/97) */
            return(rc);
        }
        vmAssert(par->ar_f & AR_HCO);
        hco = VMHcoAr(*par);
        vmAssert(hco != HCONULL);
        do {
            hco = VMHconextCo(*(pco = vmCoMap(hco)));
            if ((pco->co_hconext == HCONULL) && (vmPrivCo(pco))) {
               reload++;
               break;
            }
        } while (vmPrivCo(pco));
        hptdasrc = pco->co_hobptda;

        /* allocate new Context Record */
        rc = vmCoAlloc(SSToDS(&pco1));
        pco = pco1;         /* pass to register variable */

        /*
         * If no flags value is given, use the original object's default
         * value. This is still stored in the arena record.
         */
        if (fl == NULL) {
            fl = (par->ar_f & AR_PERMISSION) >> AR_PERMSHFT;
        };
    };
    /* rc = return code from either VMReserve or from vmCoAlloc */
    if (rc == NO_ERROR) {

        /* If tiled, copy selectors, growing LDT if needed */
        csel = (par->ar_ipg + par->ar_cpg + (WORDMASK >> PAGESHIFT))
            >> (WORDSHIFT - PAGESHIFT);
        csel -= par->ar_ipg >> (WORDSHIFT - PAGESHIFT);

        if ((par->ar_f & AR_SELMAP) == 0 ||
            ((rc = SELCopy(LaToSelTiled(par->ar_ipg << PAGESHIFT),
            (ushort_t) (csel), hptdasrc, hptda, fl)) == NO_ERROR)) {

            /*
             * Either we didn't need a selector mapping or we got it ok.
             *
             * Link in the arena or context record.  This must be done before
             * the PGAttach so the page manager can find all contexts attached
             * to a particular page.
             * Linking must be done in an atomic operation, i.e. without
             * blocking, since other threads may be walking the link list
             * without taking the semaphore.
             */
            if (pco1) {        /* CO allocated? */
                pco->co_fb = (uchar_t)((fl >> CO_PERMSHFT) & CO_PERMMASK);
                pco->co_hobptda = hptda;
                vmLinkCo(pco, par);
            }
            else {          /* AR allocating */
                vmLinkAr(par, pob);
            };

            if (reload) {
               vmAssert(vmIsLoadOb(pob));
               fl |= PG_RELOAD;
            }

            if ((rc = PGAttach(par->ar_ipg, pptda, par->ar_ipg,
                VMGetPTDAAddr(hptdasrc),
                par->ar_cpg, fl)) != NO_ERROR) {
                /*
                 * We come here if PGAttach failed.
                 * Unlink the co or ar and free the selectors.
                 */
                VMFreeMem(par->ar_ipg << PAGESHIFT, hptda, 0);
            }
        }

    }
    VMClearSem(hob);
    MMPHPostVMAttach();                         /* Performance Hook (20/97) */
    return(rc);
}
#pragma code_seg()
