/*static char *SCCSID = "@(#)vdmpage.c  6.9 92/06/03";*/

#define SCCSID "@(#)vdmpage.c   6.9 92/06/03"

/***    VDMPAGE.C
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989, 1992
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Title:
 *
 *      VDMPAGE  Exported Procedures
 *
 *      Description:
 *
 *      Exported VDH services to support page granular memory allocations
 *
 *      Modification History:
 *      10/14/80 Sudeep Bharati        Created
 *      02/11/89 bens                  Changes to EP function headers for SDR.
 *      10/29/91 Steve Best   b729110  Remove perfview counter
 *      01/09/92 MarkF                 Add Seamless support
 *      01/28/92 Bill Madden           Added VDHMapMemBlock
 *      05/26/92 Bill Madden  B735416  Temp fix for VDHMapMemBlock   6.8
 *      03/20/93 Ken Luo       063248  Add MMIO perf hooks
 */

#define FNO_NUM  FNO_VDMPAGE_C
#include <fnomvdm.h>

#define                 VDDSTRICT
#define                 INCL_NONE
#define                 INCL_VDH

#include                <vdmvdh.h>
//#include                <pvwxport.h>                                 //b729110
//#include                <perfvw.h>                                   //b729110

#ifdef MMIOPH
  #include <mmph.h>
#endif

extern  HLA             vdmid_hla;
extern  HPFH            vdmid_hpfh;
extern  ULONG           vdmid_sig;

extern  BOOL            fIsInitTime;
extern  LA              laGlobalMap;
extern  HPFH            hpfhHead;
extern  HKSEM           hksemHFault;
extern  HVDM            vdmid_HVDM;

extern  PLOCKENTRY      pMapMemLockList;                                //BN001

extern  ULONG   PRIVENTRY LHAllocPage(HLA,PVOID,ULONG,BOOL,ULONG,PVOID);
extern  BOOL    PRIVENTRY LHUnreservePages(HLA,PVOID,ULONG);
extern  BOOL    PRIVENTRY LHFreePages(HLA,PVOID,PULONG,BOOL);
extern  PVOID   PRIVENTRY LHFindFreeMem(HLA,PVOID,PULONG);
extern  ULONG   PRIVENTRY LHReallocPages(HLA,PVOID);
extern  PLR     PRIVENTRY LHQueryRecord(HLA,PVOID);
extern  BOOL    PRIVENTRY LHCheckForInstallFault(HVDM,PVOID,ULONG);
extern  VOID    EXPENTRY VDMMMemoryMappedIn(ULONG,INT);
extern  VOID    EXPENTRY VDMMMemoryUnMapped(ULONG,INT);

VOID    PRIVENTRY vdhrpUndo(ULONG,PVOID,ULONG);
PPFH    PRIVENTRY CreatePFH(PVOID,ULONG,PFN,BOOL);
VOID    PRIVENTRY freePFH(PPFH);


#pragma code_seg("SWAP32_23","CODE")
/***EP+ VDHAllocPages - Allocate memory object
 *
 *      Allocate a page-aligned page granular memory object
 *      above the DOS Heap, which occupies linear addresses 0..256K.
 *
 *      ENTRY
 *          laddr - specific linear address (see flOptions)
 *          cPages - number of pages to allocate
 *          flOptions - option flag bits
 *              VDHAP_SPECIFIC
 *                  If true, VDHAllocPages will attempt to allocate
 *                  pages starting at the linear address laddr.
 *                  Otherwise, an arbitrary linear address will be selected.
 *              VDHAP_SYSTEM   => allocate global memory
 *                  If true, the linear address will be allocated
 *                  from the system (global) area.
 *                  Otherwise, it it will be allocated from the private
 *                  area of this process.
 *              VDHAP_FIXED
 *                  If true, the allocated memory will be made fixed.
 *                  Otherwise, the memory will be swappable.
 *                  This flag may not be used if VDHAP_PHYSICAL is
 *                  specified.
 *              VDHAP_PHYSICAL
 *                  If true, the allocated linear address range will
 *                  map the physical address range starting at laddr.
 *                  Otherwise, arbitrary physical pages will be assigned.
 *                  This flag may not be used if VDHAP_FIXED is specified.
 *
 *      When an allocation made with VDHAllocPages is shrunk by
 *      VDHReallocPages, the linear range between the end of the
 *      allocation and the original end of the allocation remains
 *      available for object growth without movement.
 *
 *      Regardless of VDHReallocPages activity, all pages in the allocation
 *      retain the same properties (i.e., fixed, system, and physical)
 *
 *
 *      EXIT-SUCCESS
 *          returns non-zero address of allocated memory
 *
 *      EXIT-FAILURE
 *          returns 0
 *          VDHGetError may return:
 *              ERROR_NOT_ENOUGH_MEMORY
 *              ERROR_INVALID_BLOCK
 *          System Halt.
 *              If flOptions are invalid.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          Any allocations that are not in the terminating VDMs
 *          private area must be release using VDHFreePages.
 *
 *      CONTEXT
 *          Init
 *              VDHAP_SYSTEM is required, VDHAP_PHYSICAL may also be used.
 *          Task (If OS/2 context then VDHAP_SYSTEM must be used)
 *
 *      NOTES
 *      1.  Option Usage Summary: (Rest all are invalid)
 *          Init Time or Prot. Mode Time:
 *              VDHAP_SYSTEM                        Mandatary
 *              VDHAP_FIXED    + VDHAP_PHYSICAL     Combined or Separate
 *              VDHAP_PHYSICAL + VDHAP_FIXED        VDHAP_FIXED is discarded
 *
 *          VDM Task Time:
 *              VDHAP_SPECIFIC + VDHAP_FIXED        Combined or Separate
 *              VDHAP_SYSTEM   + VDHAP_FIXED        Combined or Separate
 *              VDHAP_SYSTEM   + VDHAP_PHYSICAL     Combined or Separate
 *              VDHAP_PHYSICAL + VDHAP_FIXED        VDHAP_FIXED is discarded
 *
 *      2.  The callers of VDHAP_SPECIFIC should reserve the range first
 *          using VDHReservePages.ERROR_INVALID_BLOCK will be returned if
 *          this is not followed. This implies that VDHAP_SPECIFIC can be
 *          used only below 1Mb+64k.
 *
 *      3.  This service does'nt protect allocations made on a range
 *          reserved by some other VDD. It is assumed that VDDs will
 *          make allocations with VDHAP_SPECIFIC only on those ranges
 *          which they have reserved earlier.
 *
 *
 *      PCODE
 *          Shutdown MVDM if context is initialization
 *                   flOptions <> VDM_AM_SYSTEM
 *          if flOptions is VDHAP_SYSTEM
 *                  call VMM for global memory and return the address
 *          get the linear arena address according to the context
 *          call LHAllocPage
 *          If allocation is going above 4MB (returned value 2)
 *                  call VMM to allocate the object above 4MB
 *                  and return address
 *          if allocation is possible below 4MB (returned value is 1)
 *                  call VMM to allocate the object and return address
 *          otherwise allocation is not possible,(returned value 0)
 *                  return FAILURE
 */

PVOID VDHENTRY VDHAllocPages(laddr,cPages,flOptions)
PVOID          laddr;
ULONG          cPages;
ULONG          flOptions;
/*end*/
{
PVOID          pLin,pPhys;
ULONG          rc = -1;
ULONG          fl=NULL;
VMAC           vmac;
HLA            hla;
VMHOB          hobowner;

#ifdef  VDHSTRICT
    // Validate context of calling
    ASSERTTASK;

    // Validate parameters
    // 1. At Init time or in OS/2 context it is required to pass VDHAP_SYSTEM
    if((ISINITTIME || ISOS2CONTEXT)
        && !(flOptions & VDHAP_SYSTEM))
            goto panic_point;

    // 2. If laddr is legitimate (Specific/Physical) then check <laddr,cPages>
    if(flOptions & VDHAP_PHYSICAL){
        if(!IS_PAGE_ALIGNED (laddr) || cPages >= MAX_SYS_PAGES)
            goto panic_point;
    }

    // Specific Allocations are allowed only below 1MB+64k
    if(flOptions & VDHAP_SPECIFIC){
        if((ULONG)laddr >= ONEMBPLUS || cPages > ONEMBPLUS/PAGESIZE ||
                ((ULONG)laddr + cPages * PAGESIZE) > ONEMBPLUS)
            goto panic_point;
    }
#endif

#ifdef MMIOPH
   MMPHPreVDHAllocPages()
#endif

    pLin=pPhys=NULL;
    if(flOptions & VDHAP_PHYSICAL){
        if(flOptions & VDHAP_SPECIFIC)
            goto panic_point;
        else{
            pPhys = laddr;
            laddr = NULL;
        }
    }

    // If Allocation required from private arena call LHAllocPage
    if(!(flOptions & VDHAP_SYSTEM)){
        if(ISINITTIME)
            hla = &laGlobalMap;
        else
            hla = vdmid_hla;
        fl = flOptions & VDHAP_SPECIFIC;

        if(flOptions & VDHAP_FIXED)
            rc = LH_ALLOC_RESIDENT;
        else
            rc = LH_ALLOC_SWAPPABLE;

        rc=LHAllocPage(hla,laddr,cPages,fl,rc,SSToDS(&pLin));
        fl = 0;
        switch(rc) {
            case LHAP_FAIL:       // Allocation not possible
                VDHSetError(ERROR_INVALID_BLOCK);
#ifdef MMIOPH
                MMPHPostVDHAllocPages()
#endif
                return FALSE;
            case LHAP_SUCCESS:    // Allocation is possible in 0-4Mb
                fl |= VMAC_LOCSPECIFIC;
                laddr = pLin;
                break;
            case LHAP_ABOVE:   // Allocation not possible in 0-4Mb
                fl |= VMAC_LOCABOVE;
                laddr = pLin;
                break;
        }
    }

    // Initialize necessary flags for VMAllocMem
    fl |= VMAC_VDM | PG_R | PG_W | PG_X | PG_U;
    hobowner = HOBNULL;

    if(flOptions & VDHAP_PHYSICAL){
        if(flOptions & VDHAP_SYSTEM)
            fl |= (PG_UVIRT | VMAC_LOCANY);
        else
            fl |= (PG_UVIRT);
    }

    else{
        pLin = laddr;
        if(flOptions & VDHAP_FIXED)
            fl |= PG_RESIDENT;
        else
            fl |= PG_SWAPPABLE;

        if(flOptions & VDHAP_SPECIFIC)
            fl |= VMAC_LOCSPECIFIC;
    }

    if(flOptions & VDHAP_SYSTEM) {
        fl |= VMAC_ARENASYS;
        if(flOptions & VDHAP_PHYSICAL)
            hobowner = PTOVIRTOWNER;
        else
            hobowner = VDHOWNER;
    }
    else
        fl |= VMAC_ARENAPRV;

    // Call VMAllocMem
    if(fl & (VMAC_LOCSPECIFIC | VMAC_LOCABOVE)) {
        vmac.ac_va = (ULONG) pLin;
    }
    if(VMAllocMem(cPages*PAGESIZE,cPages*PAGESIZE,fl,
         HOBNULL,hobowner,HOBNULL,NA,(ULONG)pPhys,SSToDS(&vmac))
         != NO_ERROR){
        VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
        if(rc == LHAP_SUCCESS)
            LHFreePages(hla,pLin,NULL,FALSE);
#ifdef MMIOPH
        MMPHPostVDHAllocPages()
#endif
        return FALSE;
    }

    // 0 is a valid return value if allocation is given at linear 0.
    // But this service returns 0 as failure value. So instead of
    // returning 0 we return curHVDM which is the same as 0 but in alias.
    // Note VMALloc can return 0 as a valid value only for VDMs.
    if (vmac.ac_va == NULL){
#ifdef MMIOPH
        MMPHPostVDHAllocPages()
#endif
        return vdmid_HVDM;
    }

    // return allocated address
#ifdef MMIOPH
    MMPHPostVDHAllocPages()
#endif
    return (PVOID)(vmac.ac_va);
panic_point:
    Panic(("VDHAllocPages: Invalid Parameter"));
#ifdef MMIOPH
   MMPHPostVDHAllocPages()
#endif
}

#pragma code_seg()
#pragma code_seg("SWAP32_20","CODE")
/***EP+ VDHReallocPages - Reallocate a memory object
 *
 *      Grow or shrink a memory object.  If linear addresses
 *      above the current object are in use, then growth
 *      can only occur if the object is allowed to move.
 *      However, if the linear range encompassed by the new size
 *      was reserved (VDHReservePages), reallocation is
 *      guaranteed without motion.  Reallocation also succeeds
 *      without movement if the object had been larger than
 *      the desired size since it last moved.
 *
 *      ENTRY
 *          laddr - Address of the object
 *          cPages - New size of the object in pages.
 *          ulReserved - Must be zero in 2.0
 *
 *      EXIT-SUCCESS
 *          returns non-zero address of reallocated memory
 *
 *      EXIT-FAILURE
 *          returns 0
 *          VDHGetError may return:
 *              ERROR_INVALID_FUNCTION
 *              ERROR_NOT_ENOUGH_MEMORY
 *              ERROR_INVALID_BLOCK
 *          System Halt.
 *              If laddr was not allocated by VDHAllocPages or
 *              VDHReallocPages, or if cPages are invalid.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          Any allocations that are not in the terminating
 *          VDMs private area must be released using VDHFreePages.
 *
 *      CONTEXT
 *          Init
 *          Task (If in OS/2 context then <laddr,cPages> must be in sys arena)
 *
 *      NOTES
 *
 *      PCODE
 *          Validate parameters
 *          check the object is global/private
 *          call VMAlloc to allocate new size object above ONEMBPLUS
 *              or in global address space
 *          if VMAlloc fails return FAILURE
 *          call PGAttach to copy the old contents
 *          call LHFreePages to free the old object in linear heap Mgr.
 *          if new allocation below 4Mb
 *            call LHAllocPage to allocate this page in linear heap Mgr.
 *          if old object below 4Mb
 *            call VMFree to free the old object
 *          return new address
 */

PVOID VDHENTRY VDHReallocPages(laddr,cPages,ulReserved)
PVOID          laddr;
ULONG          cPages;
ULONG          ulReserved;
/*end*/
{
VMHOB          hobowner;
ULONG          cbNew,ulFlag,cbAllocated,cbObject,nAttachPages,ulUndo=0,fl;
VMOD           od;
VMAC           vmac1,vmac2;
PVOID          p;
PLR            plr = NULL;

    ulReserved;                  // eliminate compiler warning

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTTASK;

    // Validate parameters
    // 1. Count of pages should noe zero and laddr should be apge aligned
    if(cPages == 0 || cPages >= MAX_SYS_PAGES || !IS_PAGE_ALIGNED(laddr) ||
        ulReserved)
        goto panic_point;

    // 2.If init time/laddr is sys arena <laddr,cPages> must be in sys arena
    if(ISINITTIME ||
       VMInMaxSysArena(laddr) ||
       ISOS2CONTEXT){
        if(!VMInMaxSysArena(laddr) ||
           !VMInMaxSysArena(cPages * PAGESIZE + (ULONG)laddr))
            goto panic_point;
    }
    else{   // Must be in private arena (shared arena not allowed)
        if(!VMInMaxPrvArena(laddr, pPTDACur) ||
           !VMInMaxPrvArena(cPages * PAGESIZE + (ULONG)laddr, pPTDACur))
            goto panic_point;
    }
#endif

#ifdef MMIOPH
   MMPHPreVDHReallocPages()
#endif

    cbNew = cPages*PAGESIZE;

    if((ULONG)laddr < LINEAR_ARENA_END){
        plr = LHQueryRecord(vdmid_hla,laddr);
        // If no such object found or it is not allocated or it has
        // earlier been brocken into smaller records because of
        // a call to VDHMapPages shut down MVDM.
        if(plr == NULL || !(plr->lr_fState & MM_ALLOCATED) ||
                          plr->lr_fState & MM_BROCKEN)
            goto panic_point;
        else{
            cbAllocated = plr->lr_cbAllocated;
            cbObject = plr->lr_cb;
            if(plr->lr_fState & MM_SWAPPABLE)
                fl = PG_SWAPPABLE;
            else
                fl = PG_RESIDENT;
        }
    }
    else{
        // Get the original size of the object
        VMLaddrOd(od) = (ULONG)laddr;
        VMPTDAOd(od) = pPTDACur;
        if(VMGetOd(NA, SSToDS(&od)) != NO_ERROR)
            Panic(("VDHReallocPages: Invalid Parameter"));
        else
            cbObject = VMSizeOd(od);

        // Get the commiitted memory size
        cbAllocated = cbObject;
        fl = VMQ_ACCESS;
        if(VMQueryMem ((ULONG)laddr,
                       SSToDS(&cbAllocated),
                       HOBNULL,
                       SSToDS(&fl)) != NO_ERROR)
            goto panic_point;

        //CHECKS: 1. Region should be reserved.
        //        2. If also committed, cbAllocated should not be zero
        //        3. If only reserved, cbAllocated and cbObject should be same.
        if(fl & VMQ_RESERVED){
            if(!(fl & PQ_COMMIT))
                if(cbAllocated != cbObject)
                    goto panic_point;
                else
                    // nothing is committed
                    cbAllocated = 0;
            else
                vdmmAssertNEQ(cbAllocated,0);
        }
        else
            vdmmAssert;

        // Get the allocation flags of this object
        nAttachPages = cbAllocated;
        cbAllocated = cbObject;
        fl = VMQ_STATE;
        if(VMQueryMem ((ULONG)laddr,
                       SSToDS(&cbAllocated),
                       HOBNULL,
                       SSToDS(&fl)) != NO_ERROR)
            goto panic_point;

        cbAllocated = nAttachPages;

        if(fl & PQ_SWAPPABLE)
            fl = PG_SWAPPABLE;
        else
            fl = PG_RESIDENT;
    }

    if(ISINSYSARENA((ULONG)laddr)){
        ulFlag = VMAC_ARENASYS;
        hobowner = VDHOWNER;
    }
    else{
        ulFlag = VMAC_ARENAPRV;
        hobowner = HOBNULL;
    }

    // Size is getting bigger than the originl object size. So reallocation
    // will allways cause object to be moved.
    if(cbNew > cbObject){

        // Just make sure that old committed size is less than the object size
        vdmmAssertGTE(cbObject, cbAllocated);

        // Initialize flags for VMAlloc as per object information
        if(!(ISINSYSARENA((ULONG)laddr))){
            // Allocate new pages
            // We have to say fl == PG_* and not fl & PG_* as these
            // are not bit fields but enumerations
            switch(LHAllocPage(vdmid_hla,
                    NULL,
                    cPages,
                    FALSE,
                    (fl == PG_RESIDENT ? LH_ALLOC_RESIDENT: LH_ALLOC_SWAPPABLE),
                    SSToDS(&p))){
            case LHAP_SUCCESS:
                    ulFlag |= VMAC_LOCSPECIFIC;
                    ulUndo |= VDHRP_UNDO_RECORD;
                    break;
            case LHAP_FAIL:
                    Panic(("VDHReallocPages:Linear heap brocken"));
            case LHAP_ABOVE:
                    (ULONG)p = LINEAR_ARENA_END;
                    ulFlag |= VMAC_LOCABOVE;
                    break;
            }
        }

        // VMAlloc Flags
        ulFlag |= ulFlag | VMAC_ALIGNPAGE | VMAC_VDM;

        vmac1.ac_va = (ULONG) p;
        if(VMAllocMem(cbNew,0,ulFlag,HOBNULL,hobowner,HOBNULL,
                      NA,NA,SSToDS(&vmac1)) != NO_ERROR){
            vdhrpUndo(ulUndo,p,cbNew);
#ifdef MMIOPH
            MMPHPostVDHReallocPages(FALSE)
#endif
            return FALSE;
        }
        ulUndo |= VDHRP_UNDO_RESERVE;
        if(ulFlag & VMAC_LOCSPECIFIC)
            vmac1.ac_va = (ulong_t) p;

        nAttachPages = cbAllocated/PAGESIZE;

        // For grown up size Commit memory
        ulFlag = (fl & ~VMQ_RESERVED) | VMS_COMMIT
                 | PG_W | PG_R | PG_X | PG_U;

        vmac2.ac_va = vmac1.ac_va + cbAllocated;
        if(VMSetMem(vmac2.ac_va, cbNew - cbAllocated,
                   HOBNULL,
                   ulFlag,
                   NA) != NO_ERROR){
            vdhrpUndo(ulUndo,(PVOID)vmac1.ac_va,cbNew);
#ifdef MMIOPH
            MMPHPostVDHReallocPages(FALSE)
#endif
            return FALSE;
        }

        // Attach old pages to this new place
        if(PGMove(PGNOFROMP(laddr),
                    pPTDACur,
                    PGNOFROMP(vmac1.ac_va),
                    pPTDACur,
                    nAttachPages,
                    vmac1.ac_hob) != NO_ERROR){
            // decommit grown size; dont care for failure
            VMSetMem(vmac2.ac_va,cbNew - cbAllocated,
                     HOBNULL,
                     VMS_DECOMMIT,
                     NA);
            vdhrpUndo(ulUndo,(PVOID)vmac1.ac_va,cbNew);
#ifdef MMIOPH
            MMPHPostVDHReallocPages(FALSE)
#endif
            return FALSE;
        }

        // Free Old pages
        if((ULONG)laddr < LINEAR_ARENA_END &&
                  LHFreePages(vdmid_hla,laddr,NULL,TRUE) == LHFP_FAIL)
            Panic(("VDHReallocPage:Linear Heap brocken"));

        // Free the old area
        if(VMFreeMem((ULONG)laddr,HOBNULL,NA) != NO_ERROR)
            Panic(("VDHReallocPage:VMFree Broken"));

#ifdef MMIOPH
        MMPHPostVDHReallocPages((PVOID)vmac1.ac_va)
#endif
        // return new address of reallocation
        return((PVOID)vmac1.ac_va);
    }

    // Object is growing or shrinking at same place with size less than
    // the original object (assured)
    else{
        if(cbAllocated == cbNew){
#ifdef MMIOPH
            MMPHPostVDHReallocPages(laddr)
#endif
            return laddr;
        }
        if(cbAllocated > cbNew){ //shrinking case
            if(VMSetMem((ULONG)laddr + cbNew, cbAllocated - cbNew,
                         HOBNULL, VMS_DECOMMIT, NA) != NO_ERROR)
                Panic(("VDHReallocPage:VMFreeMem Broken"));
            else{
                if(plr)
                    plr->lr_cbAllocated = cbNew;
#ifdef MMIOPH
                MMPHPostVDHReallocPages(laddr)
#endif
                return laddr;
            }
        }
        // Growing case ( Growing to current size but new size will be less
        // less then the size of original object)
        ulFlag |= (fl & ~VMQ_RESERVED) | VMS_COMMIT | VMAC_LOCSPECIFIC |
                  VMAC_VDM | VMAC_ALIGNPAGE | PG_W | PG_R | PG_X | PG_U;
        if(VMSetMem((ULONG)laddr + cbAllocated, cbNew - cbAllocated,
                         HOBNULL, ulFlag, NA) != NO_ERROR) {
            VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
#ifdef MMIOPH
            MMPHPostVDHReallocPages(FALSE)
#endif
            return FALSE;
        }
        if(plr)
            plr->lr_cbAllocated = cbNew;
#ifdef MMIOPH
        MMPHPostVDHReallocPages(FALSE)
#endif
        return laddr;
    }
panic_point:
    Panic(("VDHReallocPages: Invalid Parameter"));
#ifdef MMIOPH
   MMPHPostVDHReallocPages(FALSE)
#endif
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE


/***LP vdhrpUndo - Reallocation failed so undo all that has been done
 *
 *  ENTRY
 *      ulUndo - bits to indicate undo work items
 *      laddr  - laddr of new allocated object
 *      cb     - Size of new object
 *
 *  EXIT
 *      None
 */

VOID    PRIVENTRY vdhrpUndo(ulUndo,p,cb)
ULONG   ulUndo;
PVOID   p;
ULONG   cb;
{
    cb;                    // eliminate compiler warning

    if(ulUndo == NULL)
        return;
    if(ulUndo & VDHRP_UNDO_RECORD)
        if(LHFreePages(vdmid_hla,p,NULL,FALSE) != LHFP_SUCCESS)
            Panic(("Linear Heap Brocken"));
    if(ulUndo & VDHRP_UNDO_RESERVE)
        if(VMFreeMem((ULONG)p,HOBNULL,NA) != NO_ERROR)
            Panic(("VMFree: Broken"));
    return;
}
#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_21","CODE")
/***EP+ VDHFreePages - Free the memory object
 *
 *      Free a memory object.
 *      All the memory associated will be released.  This will succeed
 *      only if the linear range specified was allocated by a previous
 *      VDHAllocPages or VDHRellocatePages call.
 *
 *      ENTRY
 *          laddr - Address of the object to be freed
 *
 *      EXIT-SUCCESS
 *          None.
 *
 *      EXIT-FAILURE
 *          System Halt.
 *            Address was not created by VDHAlloc/ReallocPages.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          Any allocations made in the terminating VDMs private area,
 *          or on behalf of the terminating VDM, must be released using
 *          VDHFreePages.
 *
 *      CONTEXT
 *          Init (Global memory only)
 *          Task (If OS/2 context then laddr must be in sys arena)
 *
 *      NOTE
 *          Freeing a memory region which is not allocated will be
 *          an internal error.At init time only global memory can be
 *          freed. If the region starting at laddr has been broken into
 *          more then one region by calls to VDHMapPages then VDHfreePages
 *          should not be used. VDHFreePages looks for a record starting at
 *          laddr and whatever is the size of that record it frees that many
 *          pages.
 *
 *      PCODE
 *          if context is initialization
 *              call VMFree
 *              return
 *          get the Linear arena address
 *          call LHFreePages
 *          if returned zero // Object is global or above 4MB
 *                  call VMM to free the object
 *          call VMM to free the object
 */

VOID VDHENTRY VDHFreePages(laddr)
PVOID         laddr;
/*end*/
{
HLA           hla;
// VMOI          oi;
// ULONG         nPages;
VMOD          od;
ULONG         cb;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTTASK;

    // Validate parameter
    // 1.laddr should be page aligned
    if(!IS_PAGE_ALIGNED(laddr))
        goto panic_point;

    // 2.If init time/os2 context <laddr> must be in sys arena
    if(ISINITTIME  || ISOS2CONTEXT){
        if(!ISINSYSARENA((ULONG)laddr))
            goto panic_point;
    }
    else{   // Must be in private arena or sys arena (shared arena not allowed)
        if(ISINSHRARENA((ULONG)laddr))
            goto panic_point;
    }
#endif

#ifdef MMIOPH
   MMPHPreVDHFreePages()
#endif

    // If freeing at init time or above 4Mb
    if(ISINITTIME || (ULONG)laddr >= LINEAR_ARENA_END){
//      if(VMObjectInfo((ULONG)laddr,HOBNULL,SSToDS(&oi)) == NO_ERROR)
        od.od_laddr = (ULONG) laddr;
        od.od_pPTDA = pPTDACur;
        if(VMGetOd(NA, SSToDS(&od)) == NO_ERROR)
            if(VMFreeMem((ULONG)laddr,HOBNULL,NA) != NO_ERROR)
                goto panic_point;
            else {
#ifdef MMIOPH
                MMPHPostVDHFreePages()
#endif
                return;
        }
        else
            goto panic_point;
    }
    // Freeing from linear arena
    hla=vdmid_hla;
    if(LHFreePages(hla,laddr,SSToDS(&cb),FALSE) == LHFP_FAIL)
            goto panic_point;
    else
        if(VMFreeMem((ULONG)laddr,HOBNULL,NA) != NO_ERROR)
            goto panic_point;
#ifdef MMIOPH
   MMPHPostVDHFreePages()
#endif
    return;
panic_point:
    Panic(("VDHFreePages: Invalid Parameter"));
#ifdef MMIOPH
   MMPHPostVDHFreePages()
#endif
}
#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***EP+ VDHFindFreePages - Find the biggest linear memory region
 *
 *      Starting at the specified linear address and up to a
 *      specified size, linear memory is searched for the biggest
 *      free linear memory region.  The address and size of
 *      the biggest region found are returned.
 *
 *      ENTRY
 *          laddr - starting address of the linear range to be searched.
 *                  laddr must be in the range 0 to 1Mb+64k and page aligned.
 *          pcPages - size of range to search, in pages
 *
 *      EXIT-SUCCESS
 *          returns non-zero address of start of free range
 *              pcPages = size of region found, in pages
 *
 *      EXIT-FAILURE
 *          returns 0
 *          VDHGetError may return
 *              ERROR_NOT_ENOUGH_MEMORY
 *                  No free pages in the range specified
 *          System Halt
 *              if range specified is not within 1Mb+64k.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Init
 *              Searches global linear map.
 *          VDM Task (VDM Creation only)
 *              Searches private VDM linear map.
 *
 *      PCODE
 *          validate parameters
 *          get linear arena address according to the context and hvdm
 *          call LHFindFreeMem
 *          return the returned value
 */

PVOID VDHENTRY VDHFindFreePages(laddr,pcPages)
PVOID          laddr;
PULONG         pcPages;
/*end*/
{
HLA            hla;
PVOID          p;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTVDMTASK;

    // Validate parameters (Make it Really Strict PTRB786184)
    if(!IS_PAGE_ALIGNED(laddr) ||
       *pcPages == 0 ||
       ((ULONG)laddr >= ONEMBPLUS) ||
       (*pcPages > ONEMBPLUS / PAGESIZE) ||
       ((ULONG)laddr + *pcPages * PAGESIZE) > ONEMBPLUS)
            Panic(("VDHFindFreePages: Invalid Parameter"));
#endif

#ifdef MMIOPH
   MMPHPreVDHFindFreePages()
#endif

    // Select linear arena
    if(ISINITTIME)
        hla= &laGlobalMap;
    else
        hla=vdmid_hla;
    p=LHFindFreeMem(hla,laddr,pcPages);

    // If no memory is found *pcPages will be 0
    if(*pcPages == 0){
        VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
#ifdef MMIOPH
        MMPHPostVDHFindFreePages()
#endif
        return FALSE;
    }
#ifdef MMIOPH
   MMPHPostVDHFindFreePages()
#endif
    return p;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***EP+ VDHGetDirtyPageInfo - Get dirty page bit vector and reset bits
 *
 *      Get a bit vector for the dirty pages of a specified VDM for a
 *      specified range of pages.  The dirty bits for the range are reset.
 *
 *      These are "virtual" dirty bits, in that they remain set until
 *      cleared by this call, regardless of what swapping activity has
 *      occured.  The actual dirty bits in the page tables are reset
 *      whenever a page is swapped to the swap device and brought back
 *      into memory.  This call is valid only for linear addresses up
 *      to 1 MB + 64 Kb in a VDM.
 *
 *      ENTRY
 *          hvdm - VDM handle to which the object belongs
 *                      0 => current VDM
 *          laddr - starting address of the range of pages
 *          cPages - range (Max allowed is 32)
 *
 *      EXIT-SUCCESS
 *          returns bit vector, where bit 0 (the low-order bit) is the
 *              dirty bit for the first page.
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If hvdm is invalid, or <laddr,cPages> not in 1Mb+64k.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          Validate parameters
 *          call PGGetDirty
 */

ULONG VDHENTRY VDHGetDirtyPageInfo(hvdm,laddr,cPages)
HVDM           hvdm;
PVOID          laddr;
ULONG          cPages;
/*end*/
{
ULONG          ulBitVector;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTTASKONLY;
    CHECKHVDM(hvdm);

    // Validate parameters  (Really Strict)
    if(!IS_PAGE_ALIGNED(laddr) ||
       cPages == 0 ||
       ((ULONG)laddr >= ONEMBPLUS) ||
       (cPages > ONEMBPLUS / PAGESIZE) ||
       ((ULONG)laddr + cPages * PAGESIZE) > ONEMBPLUS)
            goto panic_point;
#endif

#ifdef MMIOPH
   MMPHPreVDHGetDirtyPageInfo()
#endif

    if(PGGetDirty(((ULONG)laddr + (ULONG)hvdm) >> PAGESHIFT,
                          QUERYPPTDA(hvdm),
                          cPages,
                          SSToDS(&ulBitVector)) == 0){
#ifdef MMIOPH
    MMPHPostVDHGetDirtyPageInfo()
#endif
    return ulBitVector;
    }
panic_point:    // PGGetDirty fails only if address is invalid so shutdown
    Panic(("VDHGetDirtyPageInfo: Invalid Parameter"));
#ifdef MMIOPH
    MMPHPostVDHGetDirtyPageInfo()
#endif
}
#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***EP+ VDHQueryFreePages - Return amount of free virtual memory
 *
 *      Returns the total amount of free virtual memory, in bytes.
 *      This is the amount of memory that could be successfully
 *      allocated.
 *
 *      ENTRY
 *          None
 *
 *      EXIT-SUCCESSS
 *          returns number of free bytes of virtual memory
 *
 *      EXIT-FAILURE
 *          None.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Task
 *
 *      NOTES
 *          (1) This is a system-wide value.  It says nothing about
 *              the virtual address space
 *
 *      PCODE
 *          Call VMQueryMaxAlloc
 */
ULONG VDHENTRY VDHQueryFreePages()
/*end*/
{
ULONG          cbFree;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTTASKONLY;
#endif

#ifdef MMIOPH
    MMPHPreVDHQueryFreePages()
#endif

    VMQueryMaxAlloc(SSToDS(&cbFree));
#ifdef MMIOPH
    MMPHPostVDHQueryFreePages()
#endif
    return cbFree;
}

/***EP+ VDHReservePages - Reserve range of linear addresses
 *
 *      Reserve range of linear address for later use with
 *      VDHMapPages or VDHAllocatePages.   One reserved area
 *      cannot contain a mixture of pages set by VDHMapPages
 *      and VDHAllocatePages, but it can successively be used
 *      with either one.
 *
 *      ENTRY
 *          laddr - Linear address to reserve
 *                  Must be within 1 Mb + 64k and page aligned.
 *          cPages - Number of pages to reserve
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          VDHGetError may return
 *              ERROR_INVALID_BLOCK
 *                  The specified range is already partially reserved or
 *                  allocated.
 *          System Halt.
 *              If laddr or cPages are invalid.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.  The VDM Manager will clean up these reservations when
 *          the VDM terminates.
 *
 *      CONTEXT
 *          Init
 *              Reservation affects the global linear map.
 *          VDM Task (VDM Creation only)
 *              Reservation affects the local linear map.
 *
 *      PCODE
 *          validate parameters
 *          get the linear arena address according to context
 *          call LHAllocPage
 *          return the returned value
 */

BOOL VDHENTRY VDHReservePages(laddr,cPages)
PVOID         laddr;
ULONG         cPages;
/*end*/
{
ULONG         rc;
HLA           hla;
PVOID         p;


#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTVDMTASK;

    // Validate Parameters (really Strict)
    if(!IS_PAGE_ALIGNED(laddr) ||
       cPages == 0 ||
       ((ULONG)laddr >= ONEMBPLUS) ||
       (cPages > ONEMBPLUS / PAGESIZE) ||
       ((ULONG)laddr + cPages * PAGESIZE) > ONEMBPLUS)
            Panic(("VDHReservePages: Invalid Parameter"));
#endif

#ifdef MMIOPH
    MMPHPreVDHReservePages()
#endif

    if(ISINITTIME)
        hla= &laGlobalMap;
    else
        hla=vdmid_hla;

    rc = LHAllocPage(hla,laddr,cPages,TRUE,LH_RESERVE,SSToDS(&p));

    // Note rc can be either VDHAP_FAIL or VDHAP_SUCCESS but
    // not VDHAP_ABOVE as laddr is below ONEMBPLUS.
    if(rc == LHAP_FAIL){
       VDHSetError(ERROR_INVALID_BLOCK);
#ifdef MMIOPH
       MMPHPostVDHReservePages()
#endif
       return FALSE ;
    }
#ifdef MMIOPH
    MMPHPostVDHReservePages()
#endif
    return TRUE;
}

#pragma code_seg()
#pragma code_seg("SWAP32_27","CODE")
/***EP+  VDHUnreservePages - UnReserve range of linear addresses
 *
 *      Unreserve the pages previously reserved with VDHReservePages.
 *      The starting address and the size must be identical to
 *      the corresponding call to VDHReservePages.
 *
 *      Any mapping made earlier on this region should be unmapped
 *      before calling this function.
 *
 *      ENTRY
 *          laddr - Linear address to unreserve
 *                  Must be less than 1Mb+64k and page aligned.
 *                  Must also match a previous VDHReservePages call.
 *          cPages - Number of pages to unreserve
 *                  Must match the corresponding parameter to
 *                      VDHReservePages.
 *
 *      EXIT-SUCCESS
 *          None
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If laddr or cPages are invalid or this combination
 *              was not used earlier in a VDHReservePages call.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *
 *      CONTEXT
 *          Init
 *              affects the global linear map.
 *          VDM Task (VDM Creation only)
 *              affects the local linear map.
 *
 *
 *      PCODE
 *          validate parameters
 *          get the linear arena address according to context
 *          call LHUnReservePages
 *          return TRUE
 */

VOID VDHENTRY VDHUnreservePages(laddr,cPages)
PVOID          laddr;
ULONG          cPages;
{
HLA           hla;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTVDMTASK;

    // Validate Parameters (Really Strict)
    if(!IS_PAGE_ALIGNED(laddr) ||
       cPages == 0 ||
       ((ULONG)laddr >= ONEMBPLUS) ||
       (cPages > ONEMBPLUS / PAGESIZE) ||
       ((ULONG)laddr + cPages * PAGESIZE) > ONEMBPLUS)
            goto panic_point;
#endif

#ifdef MMIOPH
    MMPHPreVDHUnreservePages()
#endif

    if(ISINITTIME)
        hla= &laGlobalMap;
    else
        hla=vdmid_hla;

    if(LHUnreservePages(hla,laddr,cPages*PAGESIZE) != 0){
#ifdef MMIOPH
        MMPHPostVDHUnreservePages()
#endif
        return;
    }
panic_point:
    Panic(("VDHUnreservePages: Invalid Parameter"));
#ifdef MMIOPH
    MMPHPostVDHUnreservePages()
#endif
}

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

/***EP+ VDHMapPages - Map a specified Physical/Linear Address
 *
 *      Map a linear address region in the V86 address space to:
 *          1) part of a virtual memory object
 *          2) specific physical addresses (for physical devices)
 *          3) undefined memory ("black holes")
 *          4) invalid pages (default state for reserved pages)
 *
 *      VDHMapPages changes the virtual address contents of regions
 *      in the V86 address space (below 1M+64K).  Pages in this range
 *      can be made to address 1) part of a memory object previously
 *      allocated using VDHAllocPages or VDHReallocPages; 2) physical
 *      addresses for a device; 3) "black-hole" addresses which can be
 *      read or written, but which do not retain their values; or
 *      4) invalid pages that cause faults when accessed
 *      (see VDHInstallFaultHook).
 *
 *      Mapping succeeds only if it completely replaces a previous
 *      mapping, or if the region only contains invalid pages.
 *      To change the size of a region, it must first be made invalid.
 *
 *      The target region must lie below 1M+64K and the entire target
 *      region must be contained in pages previously reserved by
 *      VDHReservePages.  VDHMapPages and VDHAllocPages can be used
 *      on the same target region, but care must be taken.  Before
 *      switching from one service to the other, the pages effected
 *      must first be made invalid.
 *
 *      If pages are left invalid, a page fault handler must be
 *      registered (VDHInstallFaultHook) to handle page faults in
 *      case the pages are accessed.
 *
 *      Both target and source addresses must be on page boundaries.
 *      If they are not, the page offset part of the address will be
 *      ignored.
 *
 *      ENTRY   pvdhms   - source region definition
 *
 *                  vdhms_laddr defines the start of the source region
 *                      for the mapping.  The region is the same length
 *                      as the target region.
 *
 *                  vdhms_hobj will be set by the service to hold
 *                      a source handle for VDHMT_LINEAR mappings.
 *                      See VDHMT_LINEAR description below.
 *
 *              pvdhmt   - target region definition
 *
 *                  vdhmt_laddr and vdhmt_cpg define the region to
 *                      be mapped.
 *
 *                  vdhmt_hmap will be set by the service to hold
 *                      a handle to be used for remapping the region.
 *                      The handle will be reset each time it is used.
 *                      vdhmt_hmap must be the value returned from the
 *                      previous VDHMapPages call for the region unless
 *                      the pages are either already invalid or else are
 *                      are being made invalid.  If either the old or new
 *                      mapping is invalid pages, vdhmt_hmap can be 0.
 *
 *                      Making a region invalid, restores the pages in
 *                      the region to their state when reserved and sets
 *                      vdhmt_hmap to 0.
 *
 *              fl     - mapping flags
 *
 *                  VDHMT_INVALID
 *                      Make target region pages invalid.
 *
 *                      vdhmt_hmap is allowed to be zero.
 *                          The service is faster when the handle is
 *                          provided, but VDD's that do not wish to
 *                          store handles can choose to map to invalid
 *                          before remapping a region.
 *
 *                      pvdhms is ignored.
 *
 *                  VDHMT_LINEAR
 *                      Map linear source region into target region.
 *
 *                      vdhms_laddr contains the linear address to
 *                          map at the start of the target region.
 *                          This can only be a page granular address in
 *                          a memory allocation that was obtained using
 *                          VDHAllocPages or VDHReallocPages.
 *                          After a successful mapping, the target
 *                          region will alias the corresponding
 *                          region of the source.
 *
 *                      vdhms_hobj must be 0 the first time a newly
 *                          allocated or reallocated source object is mapped.
 *                          On a successful mapping, vdhms_hobj is filled in.
 *                          Using this handle on the next mapping using the
 *                          same source object will speed up that mapping
 *                          call. Any start laddr in the object can be
 *                          mapped using the handle, but the returned result
 *                          must be stored after each mapping call.  VDD's
 *                          may choose to avoid storing this handle, and
 *                          always use 0, but the call will be slower.
 *
 *                  VDHMT_PHYSICAL
 *                      Map physical source region into target region.
 *
 *                      vdhms_laddr contains the physical address to
 *                          map at the start of the target region.
 *
 *                      vdhms_hobj is ignored.
 *
 *                  VDHMT_BLACK_HOLE
 *                      Map target region to black hole pages.
 *
 *                      pvdhms is ignored.
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *              ERROR_NOT_ENOUGH_MEMORY
 *              ERROR_LONGLOCK
 *              ERROR_INVALID_PARAMETER
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      PCODE
 *          call VDMMMemoryUnMapped
 *          call VMMapVDM
 *          call VDMMMemoryMappedIn
 *          translate error return value
 */
BOOL VDHENTRY VDHMapPages(PVDHMAPSOURCE pvdhms,PVDHMAPTARGET pvdhmt,ULONG fl)
/*end*/
{
    ULONG rc;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTVDMTASK;
#endif

#ifdef MMIOPH
    MMPHPreVDHMapPages()
#endif

    // Run the unmap notification chain because VMMapVDM will unmap the pages
    // before mapping them
    VDMMMemoryUnMapped(pvdhmt->vdhmt_laddr,pvdhmt->vdhmt_cpg,fl);

/***PV - Perfview trace point
 *
 *      TRACE MINOR = <PVWEI_Ct_vdm_MapPage>,
 *      TP    = @,
 *      TYPE    = (PVW),
 *      GROUP   = STRICT,
 *      DESC    = ""
 */

//  PvwCnt_strict(vdm,MapPage,0);                                      //b729110

    if (rc = VMMapVDM(fl, (PVOID)pvdhmt, (PVOID)pvdhms))
    {
      VDHSetError(rc);
    }
    else
    {
    // If the VMMapVDM works then run the map notification chain
      VDMMMemoryMappedIn(pvdhmt->vdhmt_laddr,pvdhmt->vdhmt_cpg,fl);
    }

#ifdef MMIOPH
    MMPHPostVDHMapPages()
#endif

    return (rc == 0);
}  // end VDHMapPages
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/* seamless support start */

#define VMDHPG_FLVM  (VMAC_ARENASYS | VMAC_LOCANY | VMAC_ALIGNPAGE | PG_R)

/***EP+ VDHProcessToGlobal - Alias process specific memory area in the global arena
 *
 *      Allow a block of memory which belongs to a process to be accessed
 *      globally.  The passed in address is page aligned before processing.
 *
 *      ENTRY
 *          laddr - Starting linear address
 *          cb - Number of bytes
 *          fl - Flags for the map call
 *
 *      EXIT-SUCCESS
 *          returns Global linear address
 *
 *      EXIT-FAILURE
 *          returns NULL
 *          VDHGetError will return error from map
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None
 *
 *      CONTEXT
 *          Task
 *
 *      NOTE
 *          This is not an interface for publication!
 *
 *      PCODE
 */

PVOID VDHENTRY VDHProcessToGlobal(ulong_t laddr, ulong_t cb, ulong_t fl)
{
  int     rc;                         /* return code */
  VMAC    ac;                         /* VMMapAlias return info buffer */

#ifdef MMIOPH
  MMPHPreVDHProcessToGlobal()
#endif

  if ((rc = VMMapAlias((fl & VMDHGP_WRITE) ?
      (VMDHPG_FLVM | PG_W) : VMDHPG_FLVM, NULL, VMMA_DEVHLP, NULL,
      HOBNULL, laddr & ~PAGEMASK, HOBNULL, (cb + (laddr & PAGEMASK)),
      SSToDS(&ac))) != NO_ERROR)
    {
    VDHSetError(rc);
#ifdef MMIOPH
    MMPHPostVDHProcessToGlobal()
#endif
    return((PVOID)NULL);
    }
  /* All succeeded */
#ifdef MMIOPH
  MMPHPostVDHProcessToGlobal()
#endif
  return((PVOID)((ulong_t) ac.ac_va + (laddr & PAGEMASK)));  /* return address */
}  // end VDHProcessToGlobal

#pragma END_MVDM_SWAP_CODE
/* seamless support end */

#pragma code_seg("SWAP32_23","CODE")
/***EP+ VDHInstallFaultHook - Set a fault handler for a page fault
 *
 *      Set a page fault handler for a region of linear address space.
 *      The handler will get control when a VDM touches a page in a
 *      reserved region that was invalidated by VDHMapPages.
 *      The handler will not get control if the page was made
 *      not-present by the page manager.
 *
 *      Page fault hooks are stored on a per-VDM basis.
 *
 *      ENTRY
 *          hvdm - VDM handle
 *              0 => current VDM
 *          laddr - Starting linear address
 *          cPages - Number of pages
 *          pfn - Page fault handler function
 *            VOID  HOOKENTRY pfaulth(pvdm)
 *                ENTRY
 *                    pvdm - Address of the page in which fault occurred
 *                           or the exact location within the faulted page,
 *                           see flOptions
 *                  EXIT-SUCCESS
 *                      None.
 *                  EXIT-FAILURE
 *                      None.
 *                  CONTEXT
 *                      VDM Task
 *          flOptions - option flag bits
 *                VDHIFH_ADDR - if set, then pvdm will be a byte granular
 *                              address, otherwise it will be page granular
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          VDHGetError may return:
 *              ERROR_INVALID_BLOCK
 *                  Fault handler(s) exists in the specified range already.
 *          System Halt.
 *              If hvdm,pfn is invalid, or <laddr,cPages> above 1Mb+64K
 *
 *      VDM TERMINATION IMPLICATIONS
 *          Any fault hooks for the terminating VDM must be released
 *          using VDHRemoveFaultHook.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      NOTES
 *          (1) If the fault handler wants to terminate the VDM,
 *              it must call VDHKillVDM *AND* return.
 *
 *      PCODE
 *          validate parameter
 *          find the page fault table for hvdm
 *          check that pages are not already hooked
 *          add this page fault hook in the table
 */

BOOL VDHENTRY  VDHInstallFaultHook(hvdm,laddr,cPages,pfn,flOptions)
HVDM           hvdm;
PVOID          laddr;
ULONG          cPages;
PFAULTHOOK     pfn;
BOOL           flOptions;
/*end*/
{
PHPFH          phpfh;
PPFH           ppfh,ppfhLast,ppfhNew;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTVDMTASK;
    CHECKHVDM(hvdm);

    // validate parameters
    if(pfn == NULL ||
       !IS_PAGE_ALIGNED(laddr) ||
       cPages == 0 ||
       ((ULONG)laddr >= ONEMBPLUS) ||
       (cPages > ONEMBPLUS / PAGESIZE) ||
       ((ULONG)laddr + cPages * PAGESIZE) > ONEMBPLUS ||
       (BOOL)flOptions > VDHIFH_ADDR)
        Panic(("VDHInstallFaultHook: Invalid Parameter"));
#endif

#ifdef MMIOPH
    MMPHPreVDHInstallFaultHook()
#endif

    // Grab fault handlers mutex semaphore
    if (KSEMRequestMutex (hksemHFault, -1) != NO_ERROR)
        Panic (("OpenVDD: KSEMRequestMutex"));

    // Make sure that VDD has reserved the region for which this
    // fault handler is being installed.
    if(LHCheckForInstallFault(hvdm,laddr,cPages) == FALSE)
        Panic(("VDHInstallFaultHook:Region not reserved"));

    // Get the head of page fault handler list from the instance data
    phpfh = pVDM(hvdm,PHPFH,&vdmid_hpfh);

    ppfh = phpfh->hf_ppfhFirst;

    // Scan the page fault handler list to find a place where record
    // for this handles will be added. The list is ordered in the
    // ascending order of faulting addresses.
    ppfhLast = NULL;
    while(ppfh != NULL && (ULONG)ppfh->pf_pv < (ULONG)laddr){
        ppfhLast = ppfh;
        ppfh = ppfh->pf_ppfh;
    }

    // Now check if this record will interfere with neighbour records
    if((ppfhLast != NULL &&
      (ULONG)ppfhLast->pf_pv + ppfhLast->pf_cPages*PAGESIZE > (ULONG)laddr) ||
      (ppfh != NULL &&
      (ULONG)laddr + cPages*PAGESIZE > (ULONG)ppfh->pf_pv))
        Panic(("VDHInstallFaultHook: Fault Overlaps"));

    // Create a PFH record and initialize it
    if(ppfhNew = CreatePFH(laddr,cPages,pfn,flOptions)){

        // The record is to be added in the begining of list ?
        if(ppfhLast == NULL){
            ppfhNew->pf_ppfh = phpfh->hf_ppfhFirst;
            phpfh->hf_ppfhFirst = ppfhNew;
        }
        else{
            ppfhNew->pf_ppfh = ppfhLast->pf_ppfh;
            ppfhLast->pf_ppfh = ppfhNew;
        }

        // The record is added at last ?
        if(ppfh == NULL)
            phpfh->hf_ppfhLast = ppfhNew;

        KSEMReleaseMutex (hksemHFault);
#ifdef MMIOPH
        MMPHPostVDHInstallFaultHook()
#endif
        return TRUE;
    }
    Panic(("VDHInstallFaultHook: Heap Overflow"));
#ifdef MMIOPH
    MMPHPostVDHInstallFaultHook()
#endif
}

#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
PPFH PRIVENTRY CreatePFH(laddr,cPages,pfn,flOptions)
PVOID   laddr;
ULONG   cPages;
PFN     pfn;
BOOL    flOptions;
{
PPFH    ppfh;

        if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(PFH), (VMHOB)VDHFHOOKOWNER, NA,
          (PVOID *)SSToDS(&ppfh)) != NO_ERROR)
            return NULL;
        ppfh->pf_pv = laddr;
        ppfh->pf_cPages = cPages;
        ppfh->pf_pfn = (PFAULTHOOK)pfn;
        ppfh->pf_flags = flOptions;
        return ppfh;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***EP+ VDHRemoveFaultHook - Removes hook for VDM pages
 *
 *      Removes the hook for the specified VDM page range.
 *
 *      ENTRY
 *          hvdm - VDM handle
 *              0 => current VDM
 *          laddr  - Starting linear address
 *          cPages - Number of pages
 *          pfn    - Function supplie on VDHInstallFaultHook call.
 *                      This is used to verify that the calling VDD
 *                      installed the fault hook.
 *
 *      EXIT-SUCCESS
 *          None.
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If hvdm, laddr, cPages, or pfn are invalid.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          Any fault hooks for the terminating VDM must be released.
 *
 *      CONTEXT
 *          VDM Task
 *          Task
 *
 *      PCODE
 *          Validate the parameters
 *          get the fault handler table for hvdm
 *          deinstall the entry for the pages specified
 *          if not found return failure
 */

VOID VDHENTRY VDHRemoveFaultHook(hvdm,laddr,cPages,pfn)
HVDM          hvdm;
PVOID         laddr;
ULONG         cPages;
PFAULTHOOK    pfn;
/*end*/
{
PHPFH         phpfh;
PPFH          ppfh,ppfhLast;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTTASK;
    CHECKHVDM(hvdm);

    // Validate parameters
    if(pfn == NULL ||
       !IS_PAGE_ALIGNED(laddr) ||
       cPages == 0 ||
       ((ULONG)laddr >= ONEMBPLUS) ||
       (cPages > ONEMBPLUS / PAGESIZE) ||
       ((ULONG)laddr + cPages * PAGESIZE) > ONEMBPLUS)
        Panic(("VDHRemoveFaultHook: Invalid Parameter"));
#endif

#ifdef MMIOPH
    MMPHPreVDHRemoveFaultHook()
#endif

    // Grab fault handlers mutex semaphore
    if (KSEMRequestMutex (hksemHFault, -1) != NO_ERROR)
        Panic (("OpenVDD: KSEMRequestMutex"));

    // Get the head of page fault handler list from the instance data
    phpfh = pVDM(hvdm,PHPFH,&vdmid_hpfh);
    ppfh = phpfh->hf_ppfhFirst;
    ppfhLast = NULL;

    // Search for a record whose starting address is graeter then or equal
    // to laddr
    while(ppfh != NULL && ppfh->pf_pv < laddr){
        ppfhLast = ppfh;
        ppfh = ppfh->pf_ppfh;
    }

    // Is the starting address and cPages match ? if yes release the entry
    if(ppfh && ppfh->pf_pv == laddr && ppfh->pf_cPages == cPages
             && ppfh->pf_pfn == pfn){

        // Is the record being released is first in the list ?
        if(ppfhLast == NULL)
            phpfh->hf_ppfhFirst = ppfh->pf_ppfh;
        else
            ppfhLast->pf_ppfh = ppfh->pf_ppfh;

        // Is the record being released is last in the list ?
        if(ppfh->pf_ppfh == NULL)
            phpfh->hf_ppfhLast = ppfhLast;

        // release the record
        KSEMReleaseMutex (hksemHFault);
        freePFH(ppfh);
#ifdef MMIOPH
        MMPHPostVDHRemoveFaultHook()
#endif
        return;
    }
    else
        Panic(("VDHRemoveFaultHook: Invalid Parameter"));
#ifdef MMIOPH
    MMPHPostVDHRemoveFaultHook()
#endif
}

VOID PRIVENTRY freePFH(ppfh)
PPFH    ppfh;
{
    VMFreeKHB(VM_HKH_PUB_RESRW,ppfh);
    return;
}

/***EP+ VDHAllocDMABuffer - Allocate DMA buffer
 *
 *   ENTRY
 *
 *       cbSize = Request Size
 *          if fAlign64k == 1 cbSize will always be <= 64k
 *             fAlign64k == 0 cbSize will always be <= 128k
 *
 *       fAlign64k = 1 means Buffer should be at 64k align in Physical Memory
 *                   0 means Buffer should be at 128k align in Physical Memory
 *
 *       pPhysAddr = Place where physicall address of allocation will be
 *                   returned
 *
 *   EXIT
 *
 *       SUCCESS - returns linear address of allocation (System Memory)
 *       FAILURE - returns -1
 *          VDHGetError may return:
 *              ERROR_NOT_ENOUGH_MEMORY and other VM/PG errors
 *
 *   NOTES:
 *       - Physical memory will be allocated resident and contiguous
 *       - Linear Memory range will be from System arena
 *       - Physical memory will always be in first 16Mb (in case
 *         a machine has more than that).
 *       - The contents of returned buffer are not important (i.e.
 *         no zero fill).
 *
 *   CONTEXT:
 *       VDM Task Time
 */

PVOID VDHENTRY VDHAllocDMABuffer (cbSize,fAlign64k,pPhysAddr)
ULONG   cbSize;
BOOL    fAlign64k;
PULONG  pPhysAddr;
/*end*/
{
ULONG   fl,blkno,rc;
VMAC    ac;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTTASK;

    // Check Parameters
    if (fAlign64k)
        vdmmAssertLTE (cbSize,64 * _1K);
    else
        vdmmAssertLTE (cbSize,128 * _1K);
#endif

#ifdef MMIOPH
    MMPHPreVDHAllocDMABuffer()
#endif

    // Initialize VMAllocMem Parameters
    blkno = (fAlign64k == 1 ? PGB_64KALIGN : PGB_128KALIGN);

    fl =   VMAC_ARENASYS | VMAC_LOCANY |  VMAC_ALIGNPAGE |
           PG_RESIDENT | PG_16M | PG_CONTIG | PG_R | PG_W ;

    // Call VMAllocMem to get the buffer allocation
    if ((rc = VMAllocMem (
                    cbSize,             // buffer size
                    cbSize,             // Commit size
                    fl,                 // Flags
                    NULL,               // hobptda (dont care)
                    (VMHOB)VDHOWNER,    // VDH Service
                    0,                  // Non-loader
                    NULL,               // Selmgr flags
                    blkno,              // Blockno specifies 64k or 128k align
                    SSToDS (&ac)))      // Output buffer
            != NO_ERROR){
        VDHSetError (rc);
#ifdef MMIOPH
        MMPHPostVDHAllocDMABuffer()
#endif
        return ((PVOID)-1);
    }

    // Get the physical address
    if (PGPageToPhys((ac.ac_va >> PAGESHIFT),NULL,pPhysAddr) != NO_ERROR)
        Panic(("VDHAllocDMABuffer"));

#ifdef MMIOPH
    MMPHPostVDHAllocDMABuffer()
#endif
    return ((PVOID)ac.ac_va);
}

/***EP+ VDHFreeDMABuffer - Free DMA buffer previously allocated by
 *                         VDHAllocDMABuffer.
 *
 *   ENTRY:
 *          laddr - Starting Linear Address of DMA buffer.
 *
 *   EXIT:
 *      None (panics if laddr is invalid)
 *
 *   CONTEXT:
 *       VDM Task Time
 */

VOID VDHENTRY VDHFreeDMABuffer (laddr)
PVOID   laddr;
/*end*/
{
#ifdef MMIOPH
    MMPHPreVDHFreeDMABuffer ()
#endif
    if (VMFreeMem ((ULONG)laddr,NULL,NULL) != NO_ERROR)
        Panic(("VDHFreeDMABuffer"));
#ifdef MMIOPH
    MMPHPostVDHFreeDMABuffer ()
#endif
    return;
}

/***EP VDHMapMemBlock - Map memory block to memory above One Meg
 *
 *   ENTRY:
 *       uldest   - destination address to map source into (page aligned)
 *       ulsource - source address to map from (page aligned)
 *       ulcpages - number of pages to map
 *       fl       - VPMPG_? flags associated with the new pages (destination)
 *
 *   EXIT:
 *       if success
 *          returns FALSE
 *       else
 *          returns Error Code
 *
 *   NOTES:
 *       This services maps a range of pages from the DOS (V86) address space
 *       to an address above one meg.  The destination pages must not be
 *       committed.
 *
 *       If PG_RES1000 is set, that means that we don't need to lock down the
 *       pages, only map them in.
 *
 *     BUGBUG:                                                          //BN001
 *       Because we call directly into the page manager (PGAttach) VM gets
 *       confused when it tries to move the memory block that we have aliased,
 *       because it can't account for all of the references to the page.  The
 *       stupid fix is to lock down this memory, so that we never try to move
 *       it.  EM86DestoryVDM will unlock all of the pages in pMapMemLockList.
 *       Working on a real interface to VM to map these memory blocks.
 *
 *
 *   CONTEXT:
 *       VDM Task Time
 *
 */


BOOL VDHENTRY VDHMapMemBlock(ULONG uldest, ULONG ulsource, ULONG ulcpages,
                             ULONG fl)

{
   ULONG ulrc;
   ULONG       ulcnt;          /* number of elements in array */
   PLOCKENTRY  plockentry;     /* Ptr to struc for lock entries */

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTVDMTASK;

    // Validate Parameters
    if(!IS_PAGE_ALIGNED(uldest) || !IS_PAGE_ALIGNED(ulsource) ||
       ulcpages == 0 || (uldest < ONEMBPLUS)) {
       Panic(("VDHMapMemBlock: Invalid Parameter"));
    }
#endif

#ifdef MMIOPH
   MMPHPreVDHMapMemBlock()
#endif

   if (fl & PG_RES1000) {
      ulrc = PGAttach(uldest >> PAGESHIFT, pPTDACur, ulsource >> PAGESHIFT,
                      pPTDACur, (USHORT)ulcpages, (fl & ~PG_RES1000));
   } else {

      // Begin HACK for PTR B735416, lock down destination memory pages
      if (VMAllocKHB(VM_HKH_PUB_SWAPRW, sizeof(LOCKENTRY),                 //6.8
                     (VMHOB)VDMALIASOWNER,NA,
                     SSToDS(&plockentry)) == NO_ERROR) {

         if ((ulrc = VMLockMem(ulsource, ulcpages*PAGESIZE, VML_LONG,
                               &plockentry->le_hlock,
                               (struct pagelist_s *)VM_NO_ADDR, SSToDS(&ulcnt)))
             == NO_ERROR) {

            // Add this entry to the front of the list
            plockentry->pnextlockentry = pMapMemLockList;
            pMapMemLockList = plockentry;

            ulrc = PGAttach(uldest >> PAGESHIFT, pPTDACur, ulsource >> PAGESHIFT,
                            pPTDACur, (USHORT)ulcpages, fl);
         } else {
            VMFreeKHB(VM_HKH_PUB_SWAPRW, plockentry);
         }

      } else {
         ulrc = ERROR_NOT_ENOUGH_MEMORY;
         Panic(("VDHMapMemBlock: Out of memory"));
      }                                                                    //6.8
   }

#ifdef MMIOPH
   MMPHPostVDHMapMemBlock()
#endif

   return (ulrc);
}

#pragma END_MVDM_SWAP_CODE
