/*static char *SCCSID = "@(#)vdmmem.c   6.4 92/02/06";*/

#define SCCSID "@(#)vdmmem.c    6.4 92/02/06"

/***    VDMMEM.C
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Version 1.7, for src/dos/mvdm/vdmmem.c, mvdm, mer.krnl, currbld
 *
 *      Title:
 *
 *      VDMMEM  Exported Procedures
 *
 *      Description:
 *
 *      Exported VDH services to support byte granular memory allocations
 *
 *      Modification History:
 *      14-Oct-1980 Created
 *
 * 6.4  02/06/92 CP20 B732228 Stacey Barnes Add Panic to VDHAllocDosMem
 * 6.4  03/20/93 r206 D063248 Jon Grimm     Add MMIO perf hooks
 *      09/19/94              bjn    Changed segment names for page tuning.
 *      07/19/96 Defect 141181 Gil Pratte
 *               Added the new VDH services needed by the WIN32S VDD
 *               to run WIN32S 1.25a applications.
 *
 */
#define FNO_NUM  FNO_VDMMEM_C
#include <fnomvdm.h>

#define                 VDDSTRICT
#define                 INCL_NONE
#define                 INCL_VDH

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

extern  HLA             vdmid_hla;
extern  BYTEHEAP        vdmid_DosHeap;
extern  HVDM            vdmid_HVDM;
extern  MAPID           vdmid_mi;            // 6.4

extern  HEAP            ahp[];
extern  BOOL            fIsInitTime;
extern  LA              laGlobalMap;
extern  PVOID           pVDDFixedInstStart;
extern  PVOID           pVDDSwapInstStart;
extern  PVOID           pKrnlFixed;
extern  PVOID           pKrnlSwap;
extern  PBPH            pbphHead;

extern ULONG PASCAL demSetPTDAFilesCount(USHORT);            /* 141818 */

VOID PRIVENTRY  freeBlocks(PVOID,ULONG);
PBPH PRIVENTRY  CreateBPH(ULONG,ULONG);
BOOL PRIVENTRY  LHCreateRecord(HLA,PLR,PVOID,ULONG,ULONG);
VOID PRIVENTRY  LHUpdateRecordState(PLR,ULONG);
BOOL em86AllocSel(ULONG, ULONG, PSEL, BYTE, BYTE);
VOID em86FreeSel(SEL);

#define RECORD_MATCH    0
#define RECORD_LESS     1

#pragma BEGIN_MVDM_INIT_CODE

/***LP  LHSetupInst - Setup the global map for MVDM kernel instance
 *                    records and VDD instance records. Called by VDMMInit
 *                    after VDD loading is complete.
 *
 *      ENTRY
 *          cbFixed - Fixed Instance data
 *          cbSwap  - Swap Instance data
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          Initialization
 *
 *      PCODE
 *          Scan the global arena to place instance records
 *          Make the Fixed Instance data entry
 *          Make the Swap Instance entry
 *          return
 */

VOID  PRIVENTRY LHSetupInst(cbFixed,cbSwap)
ULONG           cbFixed;
ULONG           cbSwap;
{
PLR             plr;
HLA             hla;

    hla= &laGlobalMap;
    plr=hla->la_plrLast;

    // At this point there should not be a Linear record above 1Mb+64k
    // because at init time recrds are created either in system arena
    // or below 1Mb+64k. If one is found means heap is corrupted.
    if(plr != NULL && (ULONG)END_ADDR(plr) >= ONEMBPLUS)
        VDHHaltSystem();

    // Add fixed instance data record
    if(cbFixed != 0)
        if(LHCreateRecord(hla,plr,pKrnlFixed,cbFixed,
                       MM_FINSTANCE) == NULL)
            Panic(("LHSetupInst:Heap OverFlow"));
    plr->lr_fState |= MM_ALLOCATED | MM_RESIDENT;

    //Mark the record starting at 1MB+64k to speed up later searching.
    hla->la_plrAbove1MB = plr->lr_plr;

    // Add Swappable instance data record
    plr=hla->la_plrLast;
    if(cbSwap != 0)
        if(LHCreateRecord(hla,plr,pKrnlSwap,cbSwap,
                          MM_SINSTANCE) == NULL)
            Panic(("LHSetupInst:Heap OverFlow"));
    plr->lr_plr->lr_fState |= MM_ALLOCATED | MM_SWAPPABLE;

    return;
}

#pragma END_MVDM_INIT_CODE

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

/* Linear Heap Manager procedures
 *
 *      TERMS
 *              LA - Linear Arena        (Consists of LR's)
 *              LR - Linear Record       (Denotes an Object)
 *
 */

/***LP  LHCreate - Create linear arena (LA)
 *
 *      Create a LA
 *
 *      ENTRY   None
 *
 *      EXIT
 *              SUCCESS
 *                      return HLA
 *              FAILURE
 *                      returns FALSE
 *
 *      PCODE
 *              create a LA;
 *              if any error release LA and return FALSE;
 *              retrun address of LA;
 */

HLA PRIVENTRY LHCreate()
{
HLA           hla;

        // Allocate structure
        if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(LA), (VMHOB)VDDLAOWNER, NA,
                      SSToDS(&hla)) != NO_ERROR)
            return NULL;

        // Initialize the Linear Arena
        hla->la_plrFirst=hla->la_plrLast=NULL;
        return hla;
}
#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***LP  LHCopyLA - Create a copy of a Linear Arena
 *
 *      Create exact copy of LA.
 *
 *      ENTRY
 *              hlaSource - ptr to LA to copy
 *
 *      EXIT
 *              SUCCESS
 *                  returns hla
 *              FAILURE
 *                  returns FALSE
 *
 *      PCODE
 *          call LHCreate to create new LA;
 *          for each LR in hlaSource create duplicate LR and add to new LA;
 *          return created HLA;
 */

HLA PRIVENTRY LHCopyLA(hlaSource)
HLA           hlaSource;
{
HLA           hlaDst;
PLR           plrSrc,plrDst;

        // Allocate New Linear Arena structure
        hlaDst=LHCreate();
        if(hlaDst == NULL)
            return NULL;

        // Scan the Source Linear Records and copy each to New Ones
        plrSrc=hlaSource->la_plrFirst;
        while(plrSrc != NULL){

            // Allocate New Linear Record
            if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(LR), (VMHOB)VDDLROWNER, NA,
                          SSToDS(&plrDst)) != NO_ERROR)
                return NULL;

            // Copy The fields
            plrDst->lr_pv=plrSrc->lr_pv;
            plrDst->lr_cb=plrSrc->lr_cb;
            plrDst->lr_cbAllocated=plrSrc->lr_cbAllocated;
            plrDst->lr_fState=plrSrc->lr_fState;
            plrDst->lr_sig=plrSrc->lr_sig;

            // Copy the record address which is starting at 1MB+64k to
            // halDst to speed up searches later.
            if(hlaSource->la_plrAbove1MB == plrSrc)
                hlaDst->la_plrAbove1MB = plrDst;

            // Link Linear Record to the list
            if(hlaDst->la_plrFirst == NULL)      // To be added at head
                hlaDst->la_plrFirst=hlaDst->la_plrLast=plrDst;
            else{
                hlaDst->la_plrLast->lr_plr=plrDst;
                hlaDst->la_plrLast=plrDst;
            }
            plrSrc=plrSrc->lr_plr;
        }
        plrDst->lr_plr = NULL;
        return hlaDst;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***LP  LHDestroy - Destroy linear arena (LA)
 *
 *      Destroy a LA and release LR's associated with it.
 *
 *      ENTRY
 *              hla - LA to be released
 *
 *      EXIT
 *              none
 *
 *      PCODE
 *              release each LR on LA;
 *              release LA;
 *              return;
 */

VOID PRIVENTRY LHDestroy(hla)
HLA            hla;
{
PLR            plr,plrLast;

    plr=hla->la_plrFirst;

    // Scan the Linear Record List and release each record
    while(plr != NULL){
            plrLast=plr->lr_plr;
            VMFreeKHB(VM_HKH_PUB_RESRW,(PVOID)plr);
            plr=plrLast;
    }

    // Release the Linear Arena
    VMFreeKHB(VM_HKH_PUB_RESRW,(PVOID)hla);
    return;
}

#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***LP  LHAllocPage - Allocate from linear heap
 *
 *      Allocate pages from 0-4MB linear arena.
 *      If allocation going above 4MB nothing is done to linear arena
 *      and caller is informed of the same.
 *
 *      ENTRY
 *              hla - Handle to linear arena
 *              laddr - specified linear address(valid only with fSpecific)
 *              nPages - Number of Pages
 *              fSpecific - TRUE -> Allocation at specific address (laddr)
 *              ulType - LH_ALLOC_SWAPPABLE
 *                       LH_ALLOC_RESIDENT
 *                       LH_RESERVE   (It is always at a specific address)
 *
 *      EXIT
 *              SUCCESS
 *                      returns LHAP_SUCCESS - address of allocation in paddr
 *              FAILURE
 *                      returns - LHAP_FAIL - Allocation is not possible
 *                      returns - LHAP_ABOVE - Allocation going above 4MB
 *
 *      PCODE
 *          if usType is LH_RESERVE and allocation not possible
 *              return LHAP_FAIL
 *          else update linear arena with new linear record and return address
 *          If usType is LH_ALLOC_PAGE and allocation at specific location
 *              scan the linear arena for allocation
 *                  If possible update hla and return address of allocation
 *                  else return LHAP_SUCCESS
 *          else search 1Mb+64kto 4MB for possible allocation
 *                  if possible to meet the demand
 *                      update hla and return address
 *                  else return LHAP_ABOVE;
 */

ULONG PRIVENTRY LHAllocPage(hla,laddr,nPages,fSpecific,ulType,pladdr)
HLA             hla;
PVOID           laddr;
ULONG           nPages;
BOOL            fSpecific;
ULONG           ulType;
PVOID           *pladdr;
{
ULONG           ulSize;
PLR             plrCur,plrLast;
PVOID           pStartNS;          // Non Specific Allocation start address
PVOID           p,pTop;

    ulSize=nPages*PAGESIZE;
    plrCur=hla->la_plrFirst;
    plrLast=NULL;
    pStartNS = (PVOID)((ULONG)pVDDFixedInstStart +
                        ahp[VDMAA_OT_FIXEDINSTANCE].hp_cbMax);

    // Map Local flags to Linear Record flags
    switch(ulType){
    case LH_RESERVE:
        ulType=MM_RESERVED;
        break;

    case LH_ALLOC_SWAPPABLE:
        ulType= MM_ALLOCATED | MM_SWAPPABLE;
        break;

    case LH_ALLOC_RESIDENT:
        ulType=MM_ALLOCATED | MM_RESIDENT;
        break;
    }

    // Scan the Linear Records
    while(plrCur != NULL){

        // Specific address allocation
        if(fSpecific){
            // If this record starts with laddr
            if((ULONG)plrCur->lr_pv == (ULONG)laddr){

                // If reserving, then it should fail as an object exists there.
                // If allocating then it should be reserved.
                if((ulType & MM_RESERVED) || ((ulType & MM_ALLOCATED) &&
                              !(plrCur->lr_fState & MM_RESERVED)))
                    return LHAP_FAIL;

                // If the current record is reserved make checks
                if(plrCur->lr_fState & MM_RESERVED){

                    // Reserve size should be greater than or equal to
                    // demand and region should not be already allocated
                    if(plrCur->lr_cb < ulSize ||
                       (ulType & MM_ALLOCATED &&
                        plrCur->lr_fState & MM_ALLOCATED))
                        return LHAP_FAIL;
                    else{
                        plrCur->lr_fState |= ulType;
                        plrCur->lr_cbAllocated = ulSize;
                        *pladdr=laddr;
                        return LHAP_SUCCESS;
                    }
                }

               // Allocation not possible as object exist there
                else
                    return LHAP_FAIL;
            }

            // Continue if laddr is ahead of this linear record
            if((ULONG)END_ADDR(plrCur) <= (ULONG)laddr){
                plrLast=plrCur;
                plrCur=plrCur->lr_plr;
                continue;
            }

            // If allocation possible create the record
            if(((ULONG)laddr + ulSize) <= (ULONG)plrCur->lr_pv){

                //Create the new record and link it
                if(LHCreateRecord(hla,plrLast,laddr,ulSize,ulType) == NULL)
                    return LHAP_FAIL;
                *pladdr=laddr;
                return LHAP_SUCCESS;
            }

            // allocation failed
            else
                return LHAP_FAIL;
        }

        // Non-Specific allocation case
        else{

            // Non_specific allocations will start at 1MB+64K+MVDM Instance size
            // + VDD Instance data size, So discard all records before that
            if((ULONG)plrCur->lr_pv <= (ULONG)pStartNS){
                plrLast=plrCur;
                plrCur=plrCur->lr_plr;
                continue;
            }

            // Check for possible allocation
            if(((ULONG)plrCur->lr_pv - (ULONG)END_ADDR(plrLast)) >= ulSize){

                   //Compute the address where allocation will be given
                   p = END_ADDR(plrLast);
                   if(LHCreateRecord(hla,plrLast,p,ulSize,ulType) == NULL)
                        return LHAP_FAIL;
                   *pladdr=p;
                   return LHAP_SUCCESS;
            }

            // If not possible scan ahead
            plrLast=plrCur;
            plrCur=plrCur->lr_plr;
            continue;
        }
    }

    // This is the first block of heap
    if(plrLast == NULL) {
         if(LHCreateRecord(hla,plrLast,laddr,ulSize,ulType) == NULL)
            return LHAP_FAIL;
         *pladdr = laddr;
         return LHAP_SUCCESS;
    }

    // If allocation is not crossing 4Mb limit add a record at the tail
    else{
        if(fSpecific)
            pTop = laddr;
        else
            pTop= END_ADDR(plrLast);
        if((ULONG)pTop + ulSize <= LINEAR_ARENA_END){
             if(LHCreateRecord(hla,plrLast,pTop,ulSize,ulType) == NULL)
                return LHAP_FAIL;
             *pladdr=pTop;
             return LHAP_SUCCESS;
        }
        else{
            *pladdr = (PVOID)LINEAR_ARENA_END;
            return LHAP_ABOVE;
        }
    }

}
#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***LP  LHCreateRecord - Create a record
 *
 *      Create a record
 *
 *      ENTRY
 *              hla - linear arena
 *              plrLast - linear record previous to the one to be created
 *              laddr - Starting address of new record
 *              cb - size of new record
 *              uFlag - State of object
 *
 *      EXIT
 *              SUCCESS - !NULL
 *              FAILURE - NULL
 */

BOOL PRIVENTRY LHCreateRecord(hla,plrLast,laddr,cb,ulFlag)
HLA            hla;
PLR            plrLast;
PVOID          laddr;
ULONG          cb;
ULONG          ulFlag;
{
PVOID          p;
PLR            plrCur;

    // Create Record and initilize its fields
    if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(LR), (VMHOB)VDDLROWNER, NA,
                  SSToDS(&p)) != NO_ERROR)
       return NULL;
    plrCur=(PLR)p;
    plrCur->lr_pv=laddr;
    plrCur->lr_cb=cb;
    if(ulFlag & MM_ALLOCATED)
       plrCur->lr_cbAllocated=cb;
    else
       plrCur->lr_cbAllocated=0;
    plrCur->lr_fState=ulFlag;
    plrCur->lr_sig = laddr;

    // Link this record
    // Record is to be added in the begining ?
    if(plrLast == NULL){
        plrCur->lr_plr=hla->la_plrFirst;

        // The object being added is the first object in LA ?
        if(hla->la_plrLast== NULL)
            hla->la_plrLast=plrCur;
        hla->la_plrFirst=plrCur;
        return 1;
    }

    // The object being added is last in LA
    if(plrLast->lr_plr == NULL)
        hla->la_plrLast=plrCur;
    plrCur->lr_plr=plrLast->lr_plr;
    plrLast->lr_plr=plrCur;
    return 1;
}
#pragma code_seg()
#pragma code_seg("SWAP32_21","CODE")
/***LP  LHFreePages - Free an object from LA
 *
 *      Free an object from LA allocated by LHAllocPage.
 *
 *      ENTRY
 *              hla        - handle to the linear arena
 *              laddr      - linear address of the object
 *              pcb        - If not null return in it the count of bytes
 *              fDereserve - TRUE - Dereserve the space if previously reserved
 *                           FALSE - If it was reserved keep it reserved
 *      EXIT
 *              SUCCESS
 *                   returns LHFP_SUCCESS
 *              FAILURE
 *                   returns LHFP_FAIL - laddr is in 0-4Mb but no object
 *                                       exists there;
 *                           LHFP_ABOVE - laddr is above 4Mb
 *
 *      PCODE
 *              scan the LA for laddr;
 *              return 0 if not found ;
 *              remove LR from LA
 *              free the LR
 *              return laddr;
 */

BOOL PRIVENTRY LHFreePages(hla,laddr,pcb,fDereserve)
HLA            hla;
PVOID          laddr;
PULONG         pcb;
BOOL           fDereserve;
{
PLR            plrCur,plrLast=NULL;


    // scan for the object
    if((ULONG)laddr >= ONEMBPLUS)
        plrCur = hla->la_plrAbove1MB;
    else
        plrCur = hla->la_plrFirst;
    while(plrCur != NULL){
        if(plrCur->lr_pv == laddr){
            //If the caller wants to know the current size put it in *pcb
            if(pcb != NULL)
                *pcb = plrCur->lr_cb;

            // If the record is reserved and fDereserve does'nt force it to
            // dereserve then just mark that memory is not allocated.
            if(!fDereserve && plrCur->lr_fState & MM_RESERVED){
                plrCur->lr_fState &= ~(MM_ALLOCATED|MM_SWAPPABLE|MM_RESIDENT);
                plrCur->lr_cbAllocated=0;
                return LHFP_SUCCESS;
            }

            // Object is found, It was not reserved so free it
            // Is it the first object ?
            if(plrLast == NULL)
                  hla->la_plrFirst=plrCur->lr_plr;

            // Is it the last object  ?
            if(plrCur->lr_plr == NULL)
                  hla->la_plrLast=plrLast;
            plrLast->lr_plr=plrCur->lr_plr;

            // Free the object
            VMFreeKHB(VM_HKH_PUB_RESRW,plrCur);
            return LHFP_SUCCESS;
        }
        if(plrCur->lr_pv > laddr)
            return LHFP_FAIL;
        plrLast=plrCur;
        plrCur=plrCur->lr_plr;
    }

    //else the object is either global or above 4Mb
    return LHFP_ABOVE;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE


/***LP+ LHUpdateRecordState - Update the record state as per the new
 *                            mapping.
 *
 *  ENTRY
 *      plr - record to be updated
 *      fNew  - flags of new mappings
 *
 *  EXIT
 *      None
 */
VOID    PRIVENTRY   LHUpdateRecordState(plr,fNew)
PLR     plr;
ULONG   fNew;
{
    if(fNew & VDHMT_INVALID){
        plr->lr_fState &= (~MM_ALLOCATED & ~MM_BLACKHOLE);
        plr->lr_cbAllocated = 0;
    }
    else{
        if(fNew & VDHMT_BLACK_HOLE)
            plr->lr_fState |= (MM_ALLOCATED | MM_BLACKHOLE);
        else
            plr->lr_fState |= MM_ALLOCATED ;
        plr->lr_cbAllocated = plr->lr_cb;
    }
    return;
}
#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_27","CODE")
/***LP+ LHUnreservePages - UnreservePages in linear arena and free any
 *                         existing mapping.
 *
 *  ENTRY - hla     - linear arena handle
 *          laddr   - starting address
 *          cb      - size
 *
 *  EXIT
 *      SUCCESS - 1
 *      FAILURE - 0
 *
 */
BOOL    PRIVENTRY   LHUnReservePages(hla,laddr,cb)
HLA     hla;
PVOID   laddr;
ULONG   cb;
{
PLR     plr,plrLast=NULL;
// PVOID   sig;

    plr = hla->la_plrFirst;
    // Search a matching record
    while(plr != NULL && laddr != plr->lr_pv){
        plrLast = plr;
        plr = plr->lr_plr;
    }
    //If no record matched return failure
    if(plr == NULL)
        return 0;

    if(plr->lr_cb != cb)
        return 0;

    if(plr->lr_fState & MM_ALLOCATED){
        if(VMFreeMem((ULONG)plr->lr_pv,
                  HOBNULL,
                  NA) != NO_ERROR)
            return 0;
    }

    if(plrLast)
        plrLast->lr_plr = plr->lr_plr;
    else
        hla->la_plrFirst = plr->lr_plr;
    if(plr->lr_plr == NULL)
        hla->la_plrLast = plrLast;

    VMFreeKHB(VM_HKH_PUB_RESRW,plr);
    return 1;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***LP+  LHCheckForFault - Handle page fault dispatching
 *
 *  No page fault handler was found for the given address. Check if there
 *  is no allocation in linear heap for this address,then map it to
 *  corresponding physical pages.
 *
 *  ENTRY
 *      laddr - linear address of object
 *
 *  EXIT
 *      SUCCESS
 *          returns RECORD_CREATED
 *      FAILURE
 *          returns RECORD_INVALID
 *                  RECORD_MAPPED
 *
 *  CONTEXT
 *      Current VDM task
 *
 *  PCODE:
 *      if a record exist for this paddr and it is invalid
 *              return RECORD_INVALID
 *      if a record exist for this paddr and it is mapped
 *              return RECORD_MAPPED
 *      If there is no record then create one and map it to physical pages
 *              return RECORD_CREATED
 */
ULONG   PRIVENTRY LHCheckForFault(laddr)
PVOID   laddr;
{
PLR     plrCur,plrLast;
VDHMAPTARGET vdhmt;
VDHMAPSOURCE vdhms;

        plrCur = vdmid_hla->la_plrFirst;
        plrLast = NULL;

        // pgpagefault passes byte granular address,
        // it must be page granular for this code
        (page_t)laddr = (page_t)laddr >> PAGESHIFT;
        (page_t)laddr = (page_t)laddr << PAGESHIFT;

        while(plrCur != NULL && plrCur->lr_pv <= laddr){
            plrLast = plrCur;
            plrCur = plrCur->lr_plr;
        }

        // Check that laddr is not in last record
        if(plrLast != NULL && laddr < END_ADDR(plrLast))
            if(plrLast->lr_fState == MM_RESERVED)
                return RECORD_INVALID;
            else
                return RECORD_MAPPED;
        else{
            // Create a record to show that range is mapped to physical pages.
            if(LHCreateRecord(vdmid_hla, plrLast, laddr, PAGESIZE, MM_RESERVED)
                                                                     == NULL){
                VDHKillVDM(vdmid_HVDM);
                return RECORD_CREATED;
            }

            // mark record as valid
            LHUpdateRecordState(
               (plrLast == NULL ? vdmid_hla->la_plrFirst : plrLast->lr_plr),
                MM_ALLOCATED);

            // map the pages to corresponding physical pages
            vdhms.vdhms_laddr = (ULONG)laddr;
            vdhms.vdhms_hobj = 0;
            vdhmt.vdhmt_laddr = (ULONG)laddr;
            vdhmt.vdhmt_cpg = 1;
            vdhmt.vdhmt_hmap = 0;
            VDHMapPages(SSToDS(&vdhms), SSToDS(&vdhmt), VDHMT_PHYSICAL);
            return RECORD_CREATED;
        }
}

#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***LP  LHCheckForInstallFault - Assertion check for installing fault hook
 *
 *  The region for which fault hopok is being installed must be reserved
 *  with VDHReservePages before installing a hook there.
 *
 *
 *  ENTRY
 *      hvdm  - VDM handle
 *      laddr - linear address of object
 *      cPages - count of pages
 *
 *  EXIT
 *      SUCCESS
 *          returns TRUE
 *      FAILURE
 *          returns FALSE
 *
 *  PCODE:
 *      If a record exist covering <laddr,cPages>
 *          if State is RESERVED
 *              return TRUE
 *      return FALSE
 */
BOOL   PRIVENTRY LHCheckForInstallFault(hvdm,laddr,cPages)
HVDM    hvdm;
PVOID   laddr;
ULONG   cPages;
{
PLR     plrCur;
HLA     hla;
ULONG   cb, cbAvail = 0;
PVOID   psig;
BOOL    bRet = FALSE;

        cb = cPages*PAGESIZE;
        hla = REFHVDM(hvdm,HLA,vdmid_hla);
        plrCur = hla->la_plrFirst;

        // Find the first record with end address larger than laddr
        while(plrCur != NULL && (ULONG)laddr >= (ULONG)END_ADDR(plrCur))
            plrCur = plrCur->lr_plr;

        // Make sure record contains address and is reserved
        if (plrCur != NULL && (ULONG)laddr >= (ULONG)plrCur->lr_pv
                   && plrCur->lr_fState & MM_RESERVED)  {

            cbAvail = plrCur->lr_cb - ((ULONG)laddr - (ULONG)plrCur->lr_pv);


            // add in other regions sizes if reservation was split
            for (psig = plrCur->lr_sig, plrCur = plrCur->lr_plr;
                 plrCur != NULL && plrCur->lr_sig == psig
                    && plrCur->lr_fState & MM_RESERVED;
                 plrCur = plrCur->lr_plr)  {

                    cbAvail += plrCur->lr_cb;

            }  // endfor

            bRet = cb <= cbAvail;

        }  // endif

        return(bRet);
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***LP  LHReallocPages - Realloc the object
 *
 *  ENTRY
 *      hla   - handle to linear arena
 *      laddr - linear address of object
 *
 *  EXIT
 *      SUCCESS
 *          returns linear address
 *      FAILURE
 *          returns 0 (FAILURE)
 *
 *  NOTE
 *      If object is growing it will always be moved and reallocated by
 *      VDHReallocPages without calling this routine. This routine is
 *      called only if reallocated size is less then the original allocated
 *      size. It is possible to grow the object within the original object.
 */

ULONG PRIVENTRY LHReallocPages(hla,laddr)
HLA     hla;
PVOID   laddr;
{
PLR            plrCur;
// ULONG          cbNewSize;

    if((ULONG)laddr >= ONEMBPLUS)
        plrCur = hla->la_plrAbove1MB;
    else
        plrCur=hla->la_plrFirst;

    // scan for the object
    while(plrCur != NULL){
        if(plrCur->lr_pv == laddr)
            return plrCur->lr_cbAllocated;
        if(plrCur->lr_pv > laddr)
            return NULL;
        plrCur=plrCur->lr_plr;
    }
    return NULL;
}

/***LP  LHQueryRecord - Return a record starting with laddr from the heap
 *
 *  ENTRY
 *      laddr - Linear address
 *
 *  EXIT
 *      SUCCESS - returns linear record address
 *      FAILURE - returns NULL
 */

PLR PRIVENTRY LHQueryRecord(hla,laddr)
HLA     hla;
PVOID   laddr;
{
PLR     plr;

    if((ULONG)laddr >= ONEMBPLUS)
        plr = hla->la_plrAbove1MB;
    else
        plr =hla->la_plrFirst;

    while(plr != NULL && laddr > plr->lr_pv)
        plr = plr->lr_plr;
    if(plr->lr_pv != laddr)
        return NULL;
    else
        return plr;
}
#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***LP  LHFindFreeMem - Find the biggest linear region
 *
 *      In a specified linear address range find the
 *      biggest free chunk.
 *
 *      ENTRY
 *              hla - handle to linear arena
 *              laddr - starting linear address (Page aligned)
 *              pnPages - pointer to the size value
 *
 *      EXIT
 *              laddr - starting linear address of the found region
 *                      *pnPages - Size of the region
 *
 *      NOTE
 *          VDHFindFreePages should allow to search only in 0-1Mb range
 *          but this routine will allow to search in the whole 0-4Mb
 *          range. VDHFindFreePages should check the limit of 1Mb.
 *
 *      PCODE
 *              Scan thru the hla for biggest free space in <laddr,*pnPages>;
 *              if nothing is free
 *                      return FALSE and size 0;
 *              Put size in *pnPages;
 *              return the laddr;
 */

PVOID PRIVENTRY LHFindFreeMem(hla,laddr,pnPages)
HLA             hla;
PVOID           laddr;
PULONG          pnPages;
{
ULONG           ulMax = 0,ul;
PVOID           pBegin,pEnd;
PLR             plrCur;

    plrCur = hla->la_plrFirst;
    pBegin = laddr;

    // End address of the search range can not cross Linear arena end(4Mb)
    pEnd = (PVOID)MIN(((ULONG)pBegin + *pnPages*PAGESIZE),LINEAR_ARENA_END);
    while(plrCur != NULL){

        // At any time if pBegin crosses pEnd search is over(termination cond.)
        if((ULONG)pBegin >= (ULONG)pEnd){
            *pnPages = ulMax / PAGESIZE;
            return(laddr);
        }

        // First case is that pBegin lies before plrCur. It is assured that
        // pBegin does'nt lie in previous record. Count the free space and
        // update ulMax if ulMax is less then this found free space.
        if((ULONG)pBegin < (ULONG)plrCur->lr_pv){

            // if pEnd is also before or in this record the search is over
            if((ULONG)pEnd <= (ULONG)plrCur->lr_pv){
                ul = MAX(ulMax,(ULONG)pEnd - (ULONG)pBegin);
                if(ul > ulMax){
                    ulMax = ul;
                    laddr = pBegin;
                }
            }
            else{
                ul = MAX(ulMax,(ULONG)plrCur->lr_pv - (ULONG)pBegin);
                if(ul > ulMax){
                    ulMax = ul;
                    laddr = pBegin;
                }
            }
            pBegin = END_ADDR(plrCur);
        }

        //Second case is that pBegin lies in plrCur.
        else if((ULONG)pBegin < (ULONG)END_ADDR(plrCur))
            pBegin = (PVOID)END_ADDR(plrCur);

        //Third Case  is that pBegin is after current record; so continue
        plrCur = plrCur->lr_plr;
    }
    // Coming here means searching free pages at tail
    if((ULONG)pBegin < (ULONG)pEnd){
        ul = MAX(ulMax,(ULONG)pEnd - (ULONG)pBegin);
        if(ul > ulMax){
            ulMax = ul;
            laddr = pBegin;
        }
    }
    *pnPages = ulMax / PAGESIZE;
    return(laddr);
}
#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***EP+ VDHAllocMem - Allocate small amount of memory
 *
 *      Allocates a small amount of memory on a LONG boundary.
 *      The memory is allocated from the system area, so the
 *      address is valid in all process contexts.
 *
 *      It is recommended that allocations larger than one page
 *      be accomplished with VDHAllocPages.
 *
 *      ENTRY
 *          cb        - Number of bytes to allocate
 *          flOptions - Allocation option flags
 *                          VDHAM_SWAPPABLE - if set, memory is allocated
 *                              from the swappable heap.  Otherwise, the
 *                              memory is allocated from the fixed heap,
 *                              and therefore may be accessed at hardware
 *                              interrupt time.
 *
 *      EXIT-SUCCESS
 *          returns non-zero address of allocated space
 *
 *      EXIT-FAILURE
 *          returns 0
 *          VDHGetError may return:
 *              ERROR_NOT_ENOUGH_MEMORY
 *          System Halt.
 *              If flOptions or cb are invalid.
 *
 *      CONTEXT
 *          Initialization
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          All memory allocated by a VDD for use by the terminating
 *          VDM should be freed using VDHFreeMem.
 *
 *      PCODE
 *          call alloc for word+cb
 *          if alloc fails return FALSE
 *          put the signature in dword
 *          return address  of allocation+word
 */

PVOID VDHENTRY VDHAllocMem(cb,flOptions)
ULONG          cb;
ULONG           flOptions;
/*end*/
{
PVOID          p;
ULONG          rc;

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

#ifdef MMIOPH
    MMPHPreVDHAllocMem()
#endif

    if(flOptions & VDHAM_SWAPPABLE)
        rc=VMAllocKHB(VM_HKH_PUB_SWAPRW, cb+sizeof(ULONG), (VMHOB)VDHOWNER,NA,
                      SSToDS(&p));
    else
        rc=VMAllocKHB(VM_HKH_PUB_RESRW, cb+sizeof(ULONG), (VMHOB)VDHOWNER, NA,
                      SSToDS(&p));

    // Allocate memory
    if(rc != NO_ERROR){
        VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
#ifdef MMIOPH
        MMPHPostVDHAllocMem()
#endif
        return FALSE;
    }

    // Put Signature
    if(flOptions & VDHAM_SWAPPABLE)
        *(PULONG)p=SIG_AM_SWAP;
    else
        *(PULONG)p=SIG_AM_NOSWAP;
#ifdef MMIOPH
    MMPHPostVDHAllocMem()
#endif
    return((PVOID)((ULONG)p + sizeof(ULONG)));
}
#pragma code_seg()
#pragma code_seg("SWAP32_21","CODE")
/***EP+ VDHFreeMem - Free memory
 *
 *      Free memory allocated by VDHAllocMem
 *
 *      ENTRY
 *          laddr - Address of the area to be freed.
 *                  Must have been previously allocated by VDHAllocMem.
 *
 *      EXIT-SUCCESS
 *          None
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If laddr was not an address allocated by VDHAllocMem.
 *
 *      CONTEXT
 *          Initialization
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      PCODE
 *          Check the signature in laddr-dword
 *          if a mismatch in signature suspend MVDM operation
 *          call free with laddr-dword
 */

VOID VDHENTRY VDHFreeMem(laddr)
PVOID         laddr;
/*end*/
{
PVOID         p;

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

#ifdef MMIOPH
    MMPHPreVDHFreeMem()
#endif

    // Check the signature and free the memory
    p=(PVOID)((ULONG)laddr-sizeof(ULONG));
    if(*(PULONG)p == SIG_AM_SWAP){
        VMFreeKHB(VM_HKH_PUB_SWAPRW,p);
#ifdef MMIOPH
        MMPHPostVDHFreeMem()
#endif
        return;
    }
    else if(*(PULONG)p == SIG_AM_NOSWAP){
        VMFreeKHB(VM_HKH_PUB_RESRW,p);
#ifdef MMIOPH
        MMPHPostVDHFreeMem()
#endif
        return;
    }
    Panic(("VDHFreeMem:Invalid Address"));
}

/***    VDHAllocUserMem -
 *
 *      ENTRY
 *          cb        - Number of bytes to allocate
 *          flOptions - Allocation option flags
 *
 *      EXIT-SUCCESS
 *          returns NO_ERROR (i.e., 0)
 *
 *      EXIT-FAILURE
 *          returns the non-zero return code from VMAllocMem
 *
 *      CONTEXT
 *          Initialization
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          All memory allocated by a VDD for use by the terminating
 *          VDM should be freed using VDHFreeUserMem.
 *
 *      Note:  This code may end up on the DDK for Merlin!
 */

ULONG VDHENTRY VDHAllocUserMem(cbSizeInBytes,                /* 141181 */
                               flOptions,                    /* 141181 */
                               pVMAC)                        /* 141181 */
ULONG          cbSizeInBytes;                                /* 141181 */
ULONG          flOptions;                                    /* 141181 */
VMAC           *pVMAC;                                       /* 141181 */
{                                                            /* 141181 */
   ULONG          ulRC;                                      /* 141181 */

#ifdef VDHSTRICT                                             /* 141181 */
   // Validate calling context                               /* 141181 */
   ASSERTTASK;                                               /* 141181 */
#endif                                                       /* 141181 */

   // Call VMAllocMem                                        /* 141181 */
   ulRC = VMAllocMem(cbSizeInBytes,                          /* 141181 */
                     0,          // amount physical storage to commit /* 141181 */
                     flOptions,  // allocation flags                  /* 141181 */
                     0,          // PDTA handle, or HOBNULL (0)       /* 141181 */
                     0,          // hobOwner, or HOBNULL (0)          /* 141181 */
                     0,          // hobMTE                            /* 141181 */
                     0,          // selector manager allocation flag  /* 141181 */
                     0,          // loader block number               /* 141181 */
                     pVMAC);     // pointer to input/output buffer    /* 141181 */

   if(ulRC != NO_ERROR)                                      /* 141181 */
   {                                                         /* 141181 */
       VDHSetError(ERROR_NOT_ENOUGH_MEMORY);                 /* 141181 */
   }                                                         /* 141181 */

   return(ulRC);                                             /* 141181 */
}  // end VDHAllocOtherMem

#pragma code_seg()
#pragma code_seg("SWAP32_21","CODE")
/***    VDHFreeUserMem - Free memory
 *
 *      Free memory allocated by VDHAllocUserMem
 *
 *      ENTRY
 *          pvAddr - Address of the area to be freed.
 *                   Must have been previously allocated by VDHAllocUserMem.
 *
 *      EXIT-SUCCESS
 *          Return NO_ERROR (i.e., 0)
 *
 *      EXIT-FAILURE
 *          Return the error from VMFreeMem
 *
 *      CONTEXT
 *          Initialization
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      PCODE
 */

ULONG VDHENTRY VDHFreeUserMem(pvAddr)                        /* 141181 */
PVOID         pvAddr;                                        /* 141181 */
{                                                            /* 141181 */

#ifdef VDHSTRICT                                             /* 141181 */
    // Validate calling context                              /* 141181 */
    ASSERTTASK;                                              /* 141181 */
#endif                                                       /* 141181 */

   return(VMFreeMem((ulong_t)pvAddr,   // Address to free    /* 141181 */
                    NULL,              // PTDA handle        /* 141181 */
                    VMFM_VDM));     // Call on behalf of VDM /* 141181 */

}  // end VDHFreeUserMem                                     /* 141181 */


/***    VDHQueryUserMem - Query mem
 *
 *      This function returns page granular information on a range of
 *      virtual memory.  If VMQ_ACCESS is specified, the access permiss-
 *      ions (read, write, execute, guard) and the allocation attributes
 *      (reserved, committed, private, shared) are returned.  If
 *      VMQ_STATE is specified, the page state (present, not-present
 *      in core, not-present not in core) and page type (swappable,
 *      discardable, resident, invalid) are returned.
 *
 *      ENTRY           pvAddr          - Region's linear address
 *                      pcbSize         - Pointer to region's size in bytes
 *                      pflag           - Pointer to entry and return flags
 *
 *                            Entry:
 *                                VMQ_ACCESS
 *                                  Query access permissions and allocation
 *                                  attributes
 *                            Return:
 *                                VMQ_FREE
 *                                  The specified address is not located inside
 *                                  any object, ie. the memory region is free
 *                                VMQ_BASE
 *                                  The specified address is the base address
 *                                  of an object
 *                                VMQ_RESERVED
 *                                  The region is reserved
 *                                VMQ_ARENAPRV
 *                                  The object is in the private arena
 *                                VMQ_ARENASHR
 *                                  The object is in the shared arena
 *                                PQ_COMMIT
 *                                  The object is committed
 *                                PQ_X
 *                                  Execute access to the committed range
 *                                  is allowed.
 *                                PQ_R
 *                                  Read access to the committed range
 *                                  is allowed
 *                                PQ_W
 *                                  Write access to the committed range
 *                                  is allowed
 *                                PQ_GUARD
 *                                  Access to the committed range of pages
 *                                  will cause a "guard page entered"
 *                                  condition in the subject process.
 *
 *                            Entry:
 *                                VMQ_STATE
 *                                  Query page state and type
 *                            Return:
 *                                PQ_P
 *                                  Pages are present
 *                                PQ_NPI
 *                                  Pages are not-present, and in core
 *                                PQ_NPNI
 *                                  Pages are not-present, and not in core
 *                                PQ_SWAPPABLE
 *                                  Pages are swappable
 *                                PQ_DISCARDABLE
 *                                  Pages are discardable
 *                                PQ_RESIDENT
 *                                  Pages are resident
 *                                PQ_INVALID
 *                                  pages are invalid
 *
 *      RETURN          int             - Result code (NO_ERROR = success)
 */

ULONG VDHENTRY VDHQueryUserMem(pvAddr, pcbSize, pflag)       /* 141181 */
PVOID         pvAddr;                                        /* 141181 */
PULONG        pcbSize;                                       /* 141181 */
PULONG        pflag;                                         /* 141181 */
{

#ifdef VDHSTRICT                                             /* 141181 */
    // Validate calling context                              /* 141181 */
    ASSERTTASK;                                              /* 141181 */
#endif                                                       /* 141181 */

   return(VMQueryMem((ulong_t)pvAddr,    // Address to free  /* 141181 */
                     (ulong_t *)pcbSize, // Pointer to the size /* 141181 */
                     HOBNULL,            // PTDA handle      /* 141181 */
                     (ulong_t *)pflag)); // Pointer to flag (attributes) /* 141181 */

}  // end VDHQueryUserMem                                    /* 141181 */


/***    VDHSetUserMem - Set memory attributes
 *
 *      Commit/Decommit/Set Protection for Region
 *
 *      ENTRY
 *          pvAddr  - Virtual address of region
 *          cbSize  - Regions size in bytes
 *          fl      - flags
 *
 *            See PGEXPORT.H for the #defines
 *
 *				  VMS_COMMIT
 *				    Commit memory resources
 *				  VMS_DECOMMIT
 *				    Decommit memory resources
 *				  VMS_DFLTPERM
 *				    Set access permissions to the default
 *				    value (initial).
 *				  VMS_INCPERM
 *				    Increase permissions by the specified
 *				    values, ie. "OR" the specified permissions
 *				    into the existing permissions.
 *				  PG_SKIPFREE
 *				    Ignore non-committed pages when setting
 *				    access permissions or decommitting.  If
 *				    not specified, attempting to decommit or
 *				    set access permissions on uncommitted memory
 *				    will return error.
 *				  PG_X
 *				    Execute access to the committed range
 *				    is desired
 *				  PG_R
 *				    Read access to the committed range
 *				    is desired
 *				  PG_W
 *				    Write access to the committed range
 *				    is desired
 *				  PG_GUARD
 *				    Access to the committed range of pages
 *				    is to cause a "guard page entered"
 *				    condition in the subject process.
 *				  PG_U
 *				    Note: When reserving a memory object with
 *                    user, this flag must be set when
 *                    changing a memory objects permissions.
 *
 *      RETURN int    - return code (NO_ERROR if successful)
 *
 *      CONTEXT
 *          Initialization
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      PCODE
 */

ULONG VDHENTRY VDHSetUserMem(pvAddr, cbSize, fl)             /* 141181 */
PVOID         pvAddr;                                        /* 141181 */
ULONG         cbSize;                                        /* 141181 */
ULONG         fl;                                            /* 141181 */
{

#ifdef VDHSTRICT                                             /* 141181 */
    // Validate calling context                              /* 141181 */
    ASSERTTASK;                                              /* 141181 */
#endif                                                       /* 141181 */

   return(VMSetMem( (ulong_t)pvAddr, // Address to set       /* 141181 */
                    (ulong_t)cbSize, // Size of memory region/* 141181 */
                    HOBNULL,         // PTDA handle          /* 141181 */
                    (ulong_t)fl,     // New region attributes/* 141181 */
                    0));  // Loader block # - Not Applicable /* 141181 */

} // end VDHSetUserMem                                       /* 141181 */


/***    VDHQueryArena - Query arena memory
 *
 *      Call VMQueryArenaAvailMem
 *
 *      This function returns the maximum amount of linear address
 *      space that could be reserved in the specified arena by the
 *      current task.  The value returned takes into account arena
 *      growth to its maximal extent as follows.  The private arena
 *      is assumed to have been grown up to the current dynamic lower
 *      boundary of the shared arena.  The shared arena is assumed to
 *      have been grown downwards to the maximum of all private arena
 *      reservations.  WARNING!  Physical storage availability is
 *      ignored by this function.
 *
 *      ENTRY           fl              - Flags
 *                                          VMAC_ARENAPRV
 *                                            Private arena info wanted
 *                                          VMAC_ARENASHR
 *                                            Shared arena info wanted
 *                      pcbLinAvail     - Pointer to ulong to receive
 *                                          amount of available memory
 */

VOID VDHENTRY VDHQueryArena(fWhichArena, pulSize)            /* 141181 */
ULONG        fWhichArena;                                    /* 141181 */
PULONG       pulSize;                                        /* 141181 */
{

#ifdef VDHSTRICT                                             /* 141181 */
    // Validate calling context                              /* 141181 */
    ASSERTTASK;                                              /* 141181 */
#endif                                                       /* 141181 */

   VMQueryArenaAvailMem((ulong_t)fWhichArena,                /* 141181 */
                        (ulong_t *)pulSize);                 /* 141181 */

}  // end VDHQueryArena                                      /* 141181 */


/***    VDHSetMaxFileHandles
 *
 *      Call demSetPTDAFilesCount
 *
 *      ENTRY   ulNumOfFH - New max file handles that this process
 *                          can have.  This will override the DOS
 *                          setting for the number of DOS_FILES
 *                          handles that are available for a VDM.
 *                          Note:  DOS only supports 255 handles
 *                                 but this call will allow up to
 *                                 32767 handles.
 *
 */

ULONG VDHENTRY VDHSetMaxFileHandles(ULONG ulNumOfFH)          /* 141181 */
{                                                            /* 141181 */

#ifdef VDHSTRICT                                             /* 141181 */
    // Validate calling context                              /* 141181 */
    ASSERTTASK;                                              /* 141181 */
#endif                                                       /* 141181 */

   return(demSetPTDAFilesCount((USHORT)ulNumOfFH));          /* 141181 */

}  // end VDHSetMaxFileHandles                               /* 141181 */



#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***EP+ VDHAllocDosMem - Alloc memory in DOS arena
 *
 *      Allocate a chunk of bytes from the DOS memory area.  Allocations
 *      will start from 0 and work up to 256K.  If there is not free
 *      memory in that range, the allocation will fail.  Allocations are
 *      always start on paragraph (16 byte) boundaries.
 *
 *      The memory layout will appear as:
 *
 *          +------------+ 640K
 *          |            |
 *          |            |
 *          |------------| 256k
 *          |            |
 *          |            |
 *          +------------+
 *          | DOS Data   |
 *          +------------+
 *          | DOS Code   |
 *          +------------+
 *          | IVT, etc.  |
 *          +------------+<-- start of allocations
 *
 *      ENTRY
 *          cb - size in bytes to allocate
 *
 *      EXIT-SUCCESS
 *          returns allocated address
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          VDHGetError may return:
 *              ERROR_NOT_ENOUGH_MEMORY
 *          System Halt.
 *              If cb == 0, or if called in invalid context.
 *
 *      NOTES
 *          There is no way to free memory taken from dos heap.
 *
 *      CONTEXT
 *          VDM Task (VDM Creation only)
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.  The VDM Manager will free this memory.
 *
 *      PCODE
 *          Get 0-256k as an object
 *          start allocating from 0
 *          keep track of top of allocation
 */

PVOID VDHENTRY VDHAllocDosMem(cb)
ULONG          cb;
/*end*/
{
PVOID          p;

#ifdef VDHSTRICT
    // Validate calling context
    ASSERTVDMTASKONLY;

    // Validate Create time only                        // 6.4
    if (vdmid_mi.mi_fState != VDM_BEING_CREATED)        // 6.4
    {                                                   // 6.4
        Panic(("VDM Create Time Only"));                // 6.4
    }                                                   // 6.4

    // Validate Parameters
    if(cb == 0)
        Panic(("VDHAllocDosMem:Invalid Parameter"));
#endif

#ifdef MMIOPH
    MMPHPreVDHAllocDosMem()
#endif

    //Make allocation paragraph aligned
    cb=ALIGN_OBJECT(cb,16);
    if(cb > vdmid_DosHeap.bh_cb){
        VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
#ifdef MMIOPH
        MMPHPostVDHAllocDosMem()
#endif
        return FALSE;
    }
    p = vdmid_DosHeap.bh_pCur;
    vdmid_DosHeap.bh_cb = vdmid_DosHeap.bh_cb - cb;
    vdmid_DosHeap.bh_pCur = (PVOID)((ULONG)vdmid_DosHeap.bh_pCur + cb);
#ifdef MMIOPH
    MMPHPostVDHAllocDosMem()
#endif
    return p;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***EP+ VDHCreateBlockPool - Initialize a memory block pool
 *
 *      Create and initialize a pool of blocks of the specified size.
 *      The blocks are allocated from the system area, so the block
 *      addresses are valid in all process contexts.
 *
 *      ENTRY
 *          cbBlock - block size in bytes
 *          flOptions - Allocation option flags
 *                          VDHCBP_SWAPPABLE - if set, blocks are allocated
 *                              from the swappable heap.  Otherwise, blocks
 *                              are allocated from the fixed heap, and
 *                              therefore may be accessed at hardware
 *                              interrupt time.
 *
 *      EXIT-SUCCESS
 *          returns non-zero block pool handle
 *
 *      EXIT-FAILURE
 *          returns 0
 *          VDHGetError may return:
 *              ERROR_NOT_ENOUGH_MEMORY
 *          System Halt.
 *              If flOptions is invalid, or cb == 0.
 *
 *      CONTEXT
 *          Initialization
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          Any block pools allocated by a VDD for use by the terminating
 *          VDM should be freed using VDHDestroyBlockPool.
 *
 *      PCODE
 *          Validate the parameter
 *          allocate and fill a handle to block pool
 *          return  the handle
 */

HBLOCK VDHENTRY VDHCreateBlockPool(cbBlock,flOptions)
ULONG           cbBlock;
ULONG           flOptions;
/*end*/
{
PBPH            pbph;

#ifdef VDHSTRICT
    // Validate calling context
    ASSERTTASK;

    // Parameter Validation
    if(cbBlock == 0)
        Panic(("VDHCreateBlockPool:Invalid Parameter"));
#endif

#ifdef MMIOPH
    MMPHPreVDHCreateBlockPool()
#endif

    // Create a block pool head
    if(!(pbph = CreateBPH(cbBlock,flOptions))){
        VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
#ifdef MMIOPH
        MMPHPostVDHCreateBlockPool()
#endif
        return FALSE;
    }

    // Link this record to global list of block pools
    pbph->bph_pbph=pbphHead;
    pbphHead=pbph;
#ifdef MMIOPH
    MMPHPostVDHCreateBlockPool()
#endif
    return (HBLOCK)pbph;
}

/***LP CreateBPH - Craete Block pool head and initialize it
 *
 *      ENTRY
 *          cbBlock - Block size on pool
 *
 *      EXIT
 *          SUCCESS - Block pool head
 *          FAILURE - FALSE
 *                   ERROR_NOT_ENOUGH_MEMORY
 */

PBPH PRIVENTRY CreateBPH(cbBlock,fl)
ULONG   cbBlock;
ULONG   fl;
{
PBPH    pbph;

    // Allocate Block pool head
    if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(BPH), (VMHOB)VDDBLOCKHOWNER, NA,
                  SSToDS(&pbph)) != NO_ERROR)
        return FALSE;

    // Initialize block pool fields
    pbph->bph_cb=cbBlock;
    pbph->bph_fl=fl;
    pbph->bph_pUseList=NULL;
    pbph->bph_pFreeList=NULL;

    return pbph;
}
/***EP+ VDHAllocBlock - Allocate a free block
 *
 *      Allocate a block from the specified block pool.
 *      The block is allocated from fixed or swappable memory,
 *      depending upon the VDHCreateBlockPool flOptions.
 *
 *      ENTRY
 *          hblock - Block pool from which to allocate block
 *
 *      EXIT-SUCCESS
 *          returns address of allocated block
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          VDHGetError may return:
 *              ERROR_NOT_ENOUGH_MEMORY
 *          System Halt.
 *              If hblock is invalid.
 *
 *      CONTEXT
 *          Initialization
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          Any blocks allocated by a VDD for use by the terminating
 *          VDM should be freed using VDHFreeBlock.  Any block pools
 *          created for the VDM should also be freed.
 *
 *      PCODE
 *          Validate the parameter
 *          if a free block is available
 *              delink it from free list and put on block in use list
 *              return the address of block
 *          else
 *              allocate block
 *              Link it to the blocks in use list
 *              return the address of block
 */

PVOID VDHENTRY VDHAllocBlock(hblock)
HBLOCK         hblock;
/*end*/
{
PBPH           pbphCur;
PVOID          pblock;


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

#ifdef MMIOPH
    MMPHPreVDHAllocBlock()
#endif

    // scan the pools list for hblock
    pbphCur=pbphHead;
    while(pbphCur != NULL && pbphCur != (PBPH)hblock)
      pbphCur = pbphCur->bph_pbph;

    //If no such block handle found means invalid handle. Shutdown MVDM
    if(pbphCur == NULL)
        Panic(("VDHAllocBlock:Invalid Parameter"));

    // Is there a block on freelist ? If not craete one
    if(pbphCur->bph_pFreeList != NULL){
        // Get the block from free list
        pblock = pbphCur->bph_pFreeList;
        // Relaese it from free list
        pbphCur->bph_pFreeList = (PVOID)(*(PULONG)pblock);
    }
    else if(pbphCur->bph_fl & VDHCBP_SWAPPABLE){
        if(VMAllocKHB(VM_HKH_PUB_SWAPRW, (sizeof(PVOID)+pbphCur->bph_cb),
                      (VMHOB)VDDBLOCKOWNER, NA, SSToDS(&pblock)) != NO_ERROR){
            VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
#ifdef MMIOPH
            MMPHPostVDHAllocBlock()
#endif
            return FALSE;
        }
    }
    else
        if(VMAllocKHB(VM_HKH_PUB_RESRW, (sizeof(PVOID)+pbphCur->bph_cb),
                      (VMHOB)VDDBLOCKOWNER, NA, SSToDS(&pblock)) != NO_ERROR){
            VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
#ifdef MMIOPH
            MMPHPostVDHAllocBlock()
#endif
            return FALSE;
    }

    // At this point we have a block in pblock. So link it on Inuse list
    *(PULONG)pblock = (ULONG)pbphCur->bph_pUseList;
    pbphCur->bph_pUseList = pblock;

    // Return the actual block address
#ifdef MMIOPH
    MMPHPostVDHAllocBlock()
#endif
    return (PVOID)((ULONG)pblock+sizeof(PVOID));
}

/***EP+ VDHFreeBlock - Free a block
 *
 *      Release a memory block previously allocated by VDHAllocBlock.
 *      The block is returned to the pool for reuse.
 *
 *      ENTRY
 *          hblock - block pool handle
 *          pblock - Address of the block to be freed
 *
 *      EXIT-SUCCESS
 *          None
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              This occurs when hblock is not a valid block pool handle,
 *              or when pblock is not a block allocated from this pool.
 *
 *      CONTEXT
 *          Initialization
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      NOTES
 *          The only way to reclaim memory for freed blocks is to
 *          call VDHDestroyBlockPool.
 *
 *      PCODE
 *          Scan the pools list for hblock
 *          Scan the matched pool list for pblock
 *          if any one of the above fails return error
 *          delink the block from blocks in use list and
 *          put on the free list
 */

VOID VDHENTRY VDHFreeBlock(hblock,pblock)
HBLOCK        hblock;
PVOID         pblock;
/*end*/
{
PBPH          pbph,pbphFree;
PVOID         laddr,pBlockCur,pBlockLast;


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

#ifdef MMIOPH
    MMPHPreVDHFreeBlock()
#endif

    // pbphFree is the block handle from where to free
    pbphFree=(PBPH)hblock;

    // Get the actual block address allocated by VDHAllocMem
    laddr=(PVOID)((ULONG)pblock-sizeof(PVOID));

    // Scan the pools list for hblock
    pbph=pbphHead;
    while(pbph != NULL && pbph != pbphFree)
        pbph=pbph->bph_pbph;
    if(pbph != NULL){

        // search pblock on this found blocl pool
        pBlockCur=pbph->bph_pUseList;
        pBlockLast=NULL;
        while(pBlockCur != NULL && pBlockCur != laddr){
            pBlockLast=pBlockCur;
            pBlockCur= (PVOID)(*(PULONG)pBlockCur);
        }

        // Is the block  found ?
        if(pBlockCur != NULL){

            // Is it first on the list ?
            if(pBlockLast == NULL) // delink the block from the head of list
                pbph->bph_pUseList= (PVOID)(*(PULONG)pBlockCur);
            else
                *(PULONG)pBlockLast = *(PULONG)pBlockCur;

            // Put freed block on the free list
            *(PULONG)pBlockCur = (ULONG)pbph->bph_pFreeList;
             pbph->bph_pFreeList = pBlockCur;
#ifdef MMIOPH
             MMPHPostVDHFreeBlock()
#endif
             return;
        }
#ifdef MMIOPH
             MMPHPostVDHFreeBlock()
#endif
    }
    Panic(("VDHFreeBlock:Invalid Parameter"));
}

/***EP+ VDHDestroyBlockPool - Destroy a block pool
 *
 *      Release a block pool.
 *
 *      ENTRY
 *          hblock - Block pool handle
 *
 *      EXIT-SUCCESS
 *          None
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              This occurs when hblock is not a valid block pool handle.
 *
 *      CONTEXT
 *          Initialization
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      PCODE
 *          free the blocks  on this block pool
 *          free the block pool handle
 */

VOID VDHENTRY VDHDestroyBlockPool(hblock)
HBLOCK        hblock;
/*end*/
{
PBPH          pbphCur,pbphLast;

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

#ifdef MMIOPH
    MMPHPreVDHDestroyBlockPool()
#endif

    pbphCur=pbphHead;
    pbphLast=NULL;

    // Scan the pools list for hblock
    while(pbphCur != NULL && pbphCur != (PBPH)hblock){
        pbphLast=pbphCur;
        pbphCur= pbphCur->bph_pbph;
    }

    // If hblock not found (invalid parameter) internal error
    if(pbphCur == NULL)
        Panic(("VDHDestroyBlockPool:Invalid Parameter"));

    // delink the block pool header
    if(pbphLast == NULL)
        pbphHead= pbphCur->bph_pbph;
    else
        pbphLast->bph_pbph=pbphCur->bph_pbph;

    // Free all blocks on the InUse list
    freeBlocks(pbphCur->bph_pUseList,pbphCur->bph_fl);

    // Free all blocks on free list
    freeBlocks(pbphCur->bph_pFreeList,pbphCur->bph_fl);

    // free the block pool header
    VMFreeKHB(VM_HKH_PUB_RESRW,pbphCur);

#ifdef MMIOPH
    MMPHPreVDHDestroyBlockPool()
#endif
    return;
}

/***LP  freeBlocks -  Free the blocks
 *
 *      ENTRY
 *          pBlock - starting block address
 *
 *      EXIT
 *          None
 */

VOID  PRIVENTRY freeBlocks(pBlock,fl)
PVOID   pBlock;
ULONG   fl;
{
PVOID   pBlockNext;

    while(pBlock != NULL){
        pBlockNext= (PVOID)(*(PULONG)pBlock);
        if(fl & VDHCBP_SWAPPABLE)
            VMFreeKHB(VM_HKH_PUB_SWAPRW,pBlock);
        else
            VMFreeKHB(VM_HKH_PUB_RESRW,pBlock);
        pBlock=pBlockNext;
    }
    return;
}
#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***EP+ VDHCopyMem - Linear memory to memory copy
 *
 *      Copies memory from one user linear address to another.
 *
 *      Protection faults are handled as if a user application
 *      performed the access.  Hence, writing to an invalid page
 *      causes a VDD page fault handler to gain control.  If there is
 *      no VDD fault handler, a "do not care" page is mapped in at
 *      the faulting linear address.
 *
 *      Note that touching invalid pages is allowed ONLY in the context
 *      of the VDM, i.e., a VDD may not touch invalid pages through the
 *      HVDM alias region if it not in the context of that VDM.  A VDD
 *      which violates this rule will cause the system to halt.
 *
 *      This call will yield the CPU if necessary to ensure
 *      that thread dispatch latency limit is not exceeded.
 *
 *      This call supports overlapping linear regions.  However, the
 *      results are undefined when copying between regions with aliases
 *      (via VDHMapPages) to the same physical page.
 *
 *      If necessary, VDHLockMem should be used to ensure that the
 *      status of the source and destination linear ranges does not change.
 *      This is required *only* if the source/destination is in private
 *      VDM memory (accessed, necessarily, via the HVDM), and the
 *      VDHCopyMem call is made in the context of some other process.
 *
 *
 *      ENTRY
 *          pSrc - Source address
 *          pDst - Destination address
 *          cb   - number of bytes to be copied
 *
 *      EXIT-SUCCESS
 *          None.
 *
 *      EXIT-FAILURE
 *          None.
 *
 *      CONTEXT
 *          Init
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      PCODE
 *          Validate the parameters
 *          Get Object semaphores for source and destination objects
 *          Call VMCopy to copy with yielding
 *          return TRUE
 */

VOID  VDHENTRY VDHCopyMem(pSrc,pDst,cb)
PVOID         pSrc;
PVOID         pDst;
ULONG         cb;
/*end*/
{
register    PULONG  pulSrc,pulDst;
ULONG       cbodd = cb & 0x3;
ULONG       cbeven = cb >> 2;
ULONG       cbcopy;
ULONG       i;
PUCHAR       pchSrc,pchDst;

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

#ifdef MMIOPH
    MMPHPreVDHCopyMem()
#endif

    // quick exit for no copy
    if(cb == 0) {
#ifdef MMIOPH
        MMPHPostVDHCopyMem()
#endif
        return;
       }

    pulSrc = (PULONG)pSrc;
    pulDst = (PULONG)pDst;

    // Decide the direction of copying (Important in case of
    // overlapping regions).

    if(pulSrc == pulDst) { // VDHCopyMem will make no difference
#ifdef MMIOPH
        MMPHPostVDHCopyMem()
#endif
        return;
       }

    if(pulSrc < pulDst){ // Copy from high address to low
        pulSrc = (PULONG)((ULONG)pulSrc + cb - 4);  // Highest dword of source
        pulDst = (PULONG)((ULONG)pulDst + cb - 4);  // and dst range.

        // copy cbeven dwords
        if (cbeven && cbeven <= VDHCP_NOYIELD) {
            for (i = 0; i < cbeven ; i++)
                *pulDst-- = *pulSrc--;
        }
        else {          // Copy with yielding
            while (cbeven > 0) {
                cbcopy = (cbeven < VDHCP_YIELD) ? cbeven : VDHCP_YIELD;
                cbeven -= cbcopy;
                for (i = 0 ; i < cbcopy ; i++)
                    *pulDst-- = *pulSrc--;
                TKTCYield();
            }
        }
        if (cbodd) { // Copy the remaing bytes in the begining of range
            pchSrc = (PUCHAR)((ULONG)pSrc + cbodd - 1);
            pchDst = (PUCHAR)((ULONG)pDst + cbodd - 1);
            for (i = 0 ; i < cbodd ; i++)
                *pchDst-- = *pchSrc--;
        }
    }
    else{              // Copy from low address to high
        if (cbeven <= VDHCP_NOYIELD) {
            for (i = 0; i < cbeven ; i++)
                *pulDst++ = *pulSrc++;
        }
        else {
            while (cbeven > 0) {
                cbcopy = (cbeven < VDHCP_YIELD) ? cbeven : VDHCP_YIELD;
                cbeven -= cbcopy;
                for (i = 0 ; i < cbcopy ; i++)
                    *pulDst++ = *pulSrc++;
                TKTCYield();
            }
        }
        if (cbodd) {  // Copy remaing bytes at the end of range
            pchSrc = (PUCHAR)(pulSrc);
            pchDst = (PUCHAR)(pulDst);
            for (i = 0 ;i < cbodd ; i++)
                *pchDst++ = *pchSrc++;
        }
    }

#ifdef MMIOPH
        MMPHPostVDHCopyMem()
#endif
    return;
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***EP+ VDHExchangeMem - Linear memory to memory exchange
 *
 *      Exchange the contents of two regions of linear
 *      address space.  Overlapping regions are not supported.
 *
 *      Protection faults are handled as if a user application
 *      performed the access.  Hence, writing to read-only
 *      pages or invalid page table entries either cause a
 *      VDD page fault handler to gain control, or cause
 *      the default behavior (VDM termination).
 *
 *      This call will yield the CPU if necessary to ensure
 *      that thread dispatch latency limit is not exceeded.
 *
 *      ENTRY
 *          pSrc - Source address
 *          pDst - Destination address
 *          cb - number of bytes to be exchanged
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          VDHGetError may return:
 *              ERROR_INVALID_BLOCK
 *
 *      CONTEXT
 *          Task
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      PCODE
 *          Validate parameters
 *          fail if regions overlap
 *          allocate one page temporary buffer
 *          For each page in source
 *              copy source to temp
 *              copy dest to source
 *              copy temp to dest
 *              call scheduler to yield the CPU
 */

BOOL VDHENTRY VDHExchangeMem(pSrc,pDst,cb)
PVOID         pSrc;
PVOID         pDst;
ULONG         cb;
/*end*/
{
ULONG   fl;
VMAC    vmac;

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

#ifdef MMIOPH
    MMPHPreVDHExchangeMem()
#endif

    // Parameter Validation
    if(ISINITTIME || cb == 0)
        Panic(("VDHExchangeMem:Invalid Parameter"));

    if((pDst == pSrc) || (pDst > pSrc && (ULONG)pDst < (ULONG)pSrc+cb) ||
                         (pDst < pSrc && (ULONG)pDst+cb > (ULONG)pSrc)){
        VDHSetError(ERROR_INVALID_BLOCK);
#ifdef MMIOPH
        MMPHPostVDHExchangeMem()
#endif
        return FALSE;
    }

    // Initialize necessary flags for VMAllocMem
    fl = VMAC_VDM | VMAC_LOCANY | VMAC_ARENASYS |
           PG_R | PG_W | PG_X| PG_U | PG_RESIDENT;

    if(VMAllocMem(cb,cb,fl,HOBNULL,(VMHOB)VDHOWNER,HOBNULL,NA,NA,SSToDS(&vmac))
                  != NO_ERROR){
        VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
#ifdef MMIOPH
        MMPHPostVDHExchangeMem()
#endif
        return FALSE;
    }

    VMCopy(cb,pSrc,(PVOID)vmac.ac_va);
    VMCopy(cb,pDst,pSrc);
    VMCopy(cb,(PVOID)vmac.ac_va,pDst);

    VMFreeMem((ULONG)vmac.ac_va,HOBNULL,NA);

#ifdef MMIOPH
    MMPHPostVDHExchangeMem()
#endif
    return TRUE;
}

/***EP+ VDHAllocSharedMem - Allocate from global shared region
 *
 *
 *      ENTRY
 *
 *
 *      EXIT-SUCCESS
 *
 *
 *      EXIT-FAILURE
 *
 *
 *      CONTEXT
 *
 *
 *      VDM TERMINATION IMPLICATIONS
 *
 *
 *      PCODE
 *
 *
 */

BOOL VDHENTRY VDHAllocSharedMem(ULONG cb, ULONG *lin, ULONG *sel)
{
   VMAC vmac;
   ULONG fl = VMAC_VDM | VMAC_ARENASHR | VMAC_OBSHRINK | VMAC_SHARED | PG_R | PG_X | PG_U | PG_SWAPPABLE;
   SEL s = 0xffff;

   /* Parameter check */
   if (lin)
   {
      *lin = 0;
   }
   else
   {
      VDHSetError(ERROR_INVALID_PARAMETER);
      return FALSE;
   }

   if (cb == 0)
   {
      VDHSetError(ERROR_INVALID_PARAMETER);
      return FALSE;
   }

   /* Allocation of selector is optional */
   if (sel)
   {
      *sel = 0;
      /* If selector is requested, size must be <= 64K */
      if (cb > 0x10000)
      {
         VDHSetError(ERROR_INVALID_PARAMETER);
         return FALSE;
      }
   }

   /* Allocate from GSR as read only */
   if (VMAllocMem(cb, cb, fl, HOBNULL, (VMHOB)VDHOWNER, HOBNULL, NA, NA, SSToDS(&vmac)) != NO_ERROR)
   {
      VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
      return FALSE;
   }

   /* Make writable */
   if (VMSetMem(vmac.ac_va, cb, HOBNULL, PG_X | PG_R | PG_W | PG_U | PG_ZEROFILL, NA) != NO_ERROR)
   {
      VMFreeMem(vmac.ac_va, HOBNULL, NA);
      VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
      return FALSE;
   }

   if (sel)
   {
      /* Allocate a selector which maps to lin */
      if (em86AllocSel(vmac.ac_va, cb, SSToDS(&s), (BYTE)D_DATA3, 0) == NULL)
      {
         VMFreeMem(vmac.ac_va, HOBNULL, NA);
         VDHSetError(ERROR_NOT_ENOUGH_MEMORY);
         return FALSE;
      }
      *sel = (ULONG)s;
   }

   *lin = vmac.ac_va;
   return TRUE;
}

/***EP+ VDHFreeSharedMem - Free to global shared region
 *
 *
 *      ENTRY
 *
 *
 *      EXIT-SUCCESS
 *
 *
 *      EXIT-FAILURE
 *
 *
 *      CONTEXT
 *
 *
 *      VDM TERMINATION IMPLICATIONS
 *
 *
 *      PCODE
 *
 *
 */

VOID VDHENTRY VDHFreeSharedMem(ULONG lin, ULONG sel)
{
   SEL s = (SEL)sel;

   if (s)
   {
      em86FreeSel(s);
   }
   if (lin)
   {
      VMFreeMem(lin, HOBNULL, NA);
   }
}

#pragma END_MVDM_SWAP_CODE
