/*static char *SCCSID = "@(#)vdmmheap.c 6.3 91/05/02";*/

#define SCCSID "@(#)vdmmheap.c  6.3 91/05/02"

/*
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *  Exported Routines:
 *      initAssignAddress
 *      VDMMAssignAddress
 *      VDMMDeassignAddress
 *
 *  Imported Routines:
 *
 *  History: 10-19-92 55060 S. Barnes - Increased VDD SWAPCODE to 384
 *           03-01-93 62793 S. Barnes - Increased VDD GLOBALCODE heap to 512
 *                                      By default heap allocations are not
 *                                      backed until actually used.
 */
#define FNO_NUM  FNO_VDMMHEAP_C
#include <fnomvdm.h>

#define cHeap           (sizeof(ahpi)/sizeof(HEAPINIT))
#define cbHeapBlock     65536           // size of heap block for selectors
#define cobj            (sizeof(aobj)/sizeof(ULONG))
#define MAP_OBJ_MASK    ( OBJSHARED | OBJEXEC | OBJDISCARD | OBJRESIDENT)

#define VDDSTRICT
#define INCL_NONE
#define INCL_VDH

#include <vdmvdh.h>
#define FOR_EXEHDR      1
#include <newexe.h>
#include <exe386.h>
#include <string.h>

extern PVOID    pVDDFixedInstStart;
extern PVOID    pVDDSwapInstStart;
extern LA       laGlobalMap;

// Local Procedures prototype

PVOID       PRIVENTRY heapAlloc           (PHEAP,           ULONG);
PHEAPBLOCK  PRIVENTRY heapAllocHB         (ULONG                 );
PHEAPBLOCK  PRIVENTRY heapCoalesceHB      (PHEAPBLOCK, PHEAPBLOCK);
BOOL        PRIVENTRY FreeInitHeap        (PHEAP                 );
BOOL        PRIVENTRY heapFreeHB          (PHEAPBLOCK            );
VOID        PRIVENTRY HeapFreeHeap        (PHEAPBLOCK            );
BOOL        PRIVENTRY fixAssignAddress    (VOID                  );
PVOID       PRIVENTRY vdmmGetFixup        (PVOID,           ULONG);
BOOL        PRIVENTRY vdmmCopyMVDMInstance(BOOL                  );
BOOL        PRIVENTRY vdmmCopyVDDInstance (HVDM, BOOL,      PVOID);
VOID        PRIVENTRY vdmmCVICleanup      (PHEAP                 );
PHEAP       PRIVENTRY vdmmActualCopy      (ULONG                 );
BOOL        PRIVENTRY vdmmCreateSel       (PHEAPBLOCK,PVOID,PVOID,ULONG);
INT         PRIVENTRY QueryObjType        (ULONG);
VOID        PRIVENTRY vdmmZeroFill        (PVOID,ULONG);

#pragma BEGIN_MVDM_INIT_DATA

// Heap flags for different VDD objects. VDMAA_HT_SELECTOR implies
// the VDD requires GDT selectors for every 64k block. These selectors
// used for VDD/PDD communication as PDDs are 16:16.
// VDMAA_HT_INSTANCE implies that heap will have instance data. Instance
// objects are loaded in global arena and the fixups are made in the
// VDMs private address space. These heaps provide the address where the
// objects are loaded. In case of instance heaps the selectors are faked.
// The actual selectors are created at VDM creation time. These heaps just
// keep the count of selectors for instance heaps.

HEAPINIT ahpi[] = {
    {VDMAA_HT_INITCODE                                            , 64*1024},
    {VDMAA_HT_INITDATA     | VDMAA_HT_SELECTOR                    , 64*1024},
    {VDMAA_HT_GLOBALCODE                                          , 512*1024},
    {VDMAA_HT_GLOBALDATA   | VDMAA_HT_SELECTOR                    , 128*1024},
    {VDMAA_HT_FIXEDINSTANCE| VDMAA_HT_SELECTOR | VDMAA_HT_INSTANCE, 128*1024},
    {VDMAA_HT_SWAPCODE                                            , 384*1024},
    {VDMAA_HT_SWAPDATA     | VDMAA_HT_SELECTOR                    , 128*1024},
    {VDMAA_HT_SWAPINSTANCE | VDMAA_HT_SELECTOR | VDMAA_HT_INSTANCE, 128*1024}
};

// EXE object flags associated with each heap
ULONG aobj[] = {
    { OBJSHARED | OBJEXEC | OBJRESIDENT | OBJDISCARD  }, //Init Code
    { OBJSHARED           | OBJRESIDENT | OBJDISCARD  }, //Init Data
    { OBJSHARED | OBJEXEC | OBJRESIDENT               }, //Global Code(Resident)
    { OBJSHARED           | OBJRESIDENT               }, //Global data(Resident)
    { NULL                | OBJRESIDENT               }, //Inst. data (Resident)
    { OBJSHARED | OBJEXEC                             }, //Global Code(Swap)
    { OBJSHARED                                       }, //Global Data(Swap)
    { NULL                                            }  //Inst. Data (Swap)
};

#pragma END_MVDM_INIT_DATA

#pragma BEGIN_MVDM_SWAP_DATA

// VDD EXE object heaps
HEAP    ahp[cHeap];

#pragma END_MVDM_SWAP_DATA

#pragma BEGIN_MVDM_INIT_CODE

/***LP+ initAssignAddress - Initialize Data structrues for AssignAddress
 *
 *      ENTRY
 *          None
 *
 *      EXIT
 *          SUCCESS - returns TRUE
 *          FAILURE - returns FALSE
 */

BOOL    PRIVENTRY initAssignAddress()
/*end*/
{
PVOID       laddr;
ULONG       cb,i;
PHEAPBLOCK  phb;
ULONG       fl=0,flsel;
VMAC        vmac;

    // initialize each heap
    for (i=0; i<cHeap; i++) {
        flsel =0;
        ahp[i].hp_fl = ahpi[i].hpi_fl;  // set heap type flags
        ahp[i].hp_cbMax = ahpi[i].hpi_cb;  // set max size

        // compute size of initial free block

        if (ahp[i].hp_fl & VDMAA_HT_SELECTOR) { // have to do 64K boundaries
            cb = cbHeapBlock;
            ahp[i].hp_cbFreeHeapBlock = cb;
        }
        else {
            cb = ahp[i].hp_cbMax;
            ahp[i].hp_cbFreeHeapBlock = cb; // only one free heap block
        }

        // get starting linear address of heap

        fl = VMAC_LOCANY |VMAC_ALIGNSEL |VMAC_ARENASYS;

        // for non instance heap having selector property allocate a
        // GDT selector also.
        if (ahp[i].hp_fl & VDMAA_HT_SELECTOR &&
                         !(ahp[i].hp_fl & VDMAA_HT_INSTANCE)){
            fl |= VMAC_SELALLOC;
            flsel = SELAL_W | SELAL_R;
        }

        // Reserve global space
        if(VMAllocMem(ahp[i].hp_cbMax,0,fl,HOBNULL, (VMHOB)VDDHEAPOWNER,
             HOBNULL,flsel,NA,SSToDS(&vmac)) != NO_ERROR)
//BUGBUG sudeepb : Add undo work
            return FALSE;
        laddr = (PVOID)vmac.ac_va;

        // put a free block on heap to simplify VDMMAssignAddress

        phb = heapAllocHB(cb);
        if (phb == NULL) {
//BUGBUG sudeepb : Add undo work
            return FALSE;
        }

        if (flsel)
            phb->hb_sel = vmac.ac_sel;      // set selector
        else
            phb->hb_sel = 0;                // set "fake" selector

        phb->hb_pv = laddr;                 // set start of free block
        ahp[i].hp_pv = laddr;               // set start of heap
        ahp[i].hp_phb = phb;                // put free block in heap
    }
    return TRUE;
}

/***LP+ fixAssignAddress - Fix up Heap structures after VDDs are loaded
 *
 *     For each heap release extra space and fix up their
 *     hp_cbMax field to store the actual space used in
 *     that heap. For Init Code and Init Data the heaps
 *     will be destroyed.
 *
 *     ENTRY
 *          None
 *
 *     EXIT
 *          SUCCESS - TRUE
 *          FAILURE - FALSE
 */

BOOL PRIVENTRY fixAssignAddress()
/*end*/
{
PHEAPBLOCK phb,phbFree,phbLast;
ULONG      i,cb,cbReserve,cbFree;
// PVOID      laddr;

    //Process each heap
    for(i=0 ; i<cHeap ; i++){

        // In case of Init code and init data throw away the heap
        // and decommit+dereserve the whole space
        if(ahp[i].hp_fl & VDMAA_HT_INITCODE ||
                  ahp[i].hp_fl & VDMAA_HT_INITDATA)
            if(FreeInitHeap(&ahp[i]) == FALSE)
                return FALSE;
            else
                continue;
        else{
            // Compute the actual size of heap
            // cbFree is used to take care of last consecutive free blocks
            cbReserve=cbFree=0;
            phbFree=phbLast=NULL;
            phb=ahp[i].hp_phb;                          // First heap block
            while(phb != NULL){

                // Add bytes for all blocks whether free or in use
                // except the last free block. To tackle the problem
                // of last free consecutive blocks any free block 'cb'
                // will be stored in cbFree and on encountering a used
                // block it will be added to cb.
                if(!phb->hb_fFree){
                    phbLast = phb;
                    cbReserve += (phb->hb_cb + cbFree);
                    cbFree=0;
                    phbFree=NULL;
                }
                else{
                    cbFree += phb->hb_cb;
                    if(phbFree == NULL)
                        phbFree = phb;
                }
                phb=phb->hb_phb;
            }

            // Store the original cbReserve for freeing purpose
            // and store the actual used space
            cb=ahp[i].hp_cbMax;
            ahp[i].hp_cbMax=cbReserve;

//  In VMAllocMem it states that memory allocated by it cannot be
//  subsequently changed. As far as VM management is concerned you
//  own the memory and it does not make since to allow virtual
//  memory to be freed up incrementally. Also, the allocations
//  are not committed. So, the left over space doesn't effect
//  the size of the swap file either.
#ifdef NEVER
            // dereserve unused space

            // As VMFreeMem is page granular round up the end
            // address to next page aligned
            cbReserve = ROUND_UP(cbReserve,PAGESIZE);
            laddr = (PVOID)((ULONG)ahp[i].hp_pv + cbReserve);

            // Compute how much the space to be released
            cb = (ULONG)ahp[i].hp_pv + cb - (ULONG)laddr;
            if(VMFreeMem((ULONG)laddr,HOBNULL,NA) != NO_ERROR)
                    return FALSE;
#endif
            // Release free heap blocks at tail if any
            HeapFreeHeap(phbFree);
            phbLast->hb_phb = NULL;
        }
    }
    return TRUE;
}

/***LP+ FreeInitHeap - free init code and data heaps
 *
 *  ENTRY
 *      php - heap to be freed
 *
 *  EXIT
 *      None
 */
BOOL PRIVENTRY  FreeInitHeap(php)
PHEAP  php;
/*end*/
{

    if(VMFreeMem((ULONG)php->hp_pv,HOBNULL,NA) != NO_ERROR)
        return FALSE;
    HeapFreeHeap(php->hp_phb);
    return TRUE;
}

/***LP+ HeapFreeHeap - Free a list of Heap Blocks
 *
 *      ENTRY
 *          phb - Starting heap block
 *
 *      EXIT
 *          None
 */
VOID PRIVENTRY  HeapFreeHeap(phb)
PHEAPBLOCK      phb;
/*end*/
{
PHEAPBLOCK      phbNext;

    while(phb != NULL){
        phbNext=phb->hb_phb;
        heapFreeHB(phb);
        phb=phbNext;
    }
}

/***LP+ QueryObjType - query the object type
 *
 *     ENTRY
 *          flObj - object flag
 *
 *     EXIT
 *          SUCCESS
 *              return Object type
 *          FAILURE
 *              returns 0
 */
INT  PRIVENTRY QueryObjType(flObj)
ULONG   flObj;
/*end*/
{
ULONG   fl;
ULONG   i;

    // Mask off the undesired object flags
    fl = flObj & MAP_OBJ_MASK;

    // Match the flags to get the heap
    for(i = 0; i < cobj ; i++)
        if(aobj[i] == fl)
            return i+1;
    return 0;
}

/***EP+ VDMMAssignAddress - Assign linear address to VDD EXE object
 *
 *      Assign a linear address to hold the object requested.  The
 *      object type is used to select the heap from which the
 *      address is taken.
 *
 *      ENTRY   cb - size of object in bytes
 *              flObj - object flags from module header
 *              pfixup - address where fixup address will be placed
 *
 *      EXIT    SUCCESS, returns handle to assign address structure
 *              FAILURE, returns NULL
 */
PVOID   EXPENTRY VDMMAssignAddress(cb, flObj, pfixup)
ULONG   cb;
ULONG   flObj;
PVOID   pfixup;
/*end*/
{
PHEAP   pheap;
INT     iType;
PVOID   pload;

    iType = QueryObjType(flObj);
    if(iType == 0)
        return NULL;

    pheap = &(ahp[iType-1]);            // get heap

    // Get the loading address
    if((pload = heapAlloc(pheap,cb)) == NULL)
        return NULL;

    // Get the fixup address for instance objects. For others
    // fixup and load addresses are same.
    *(PULONG)pfixup = (ULONG)vdmmGetFixup(pload,iType-1);
    return pload;
}

/***LP+ vdmmGetFixup - Get fixup address for vdd objects
 *
 *      ENTRY   pload - load address assigned from heap
 *              iType - heap type
 *
 *      EXIT    SUCCESS, return fixup address
 *
 *      PCODE
 *              if instance heaps calculate fixup address relative to
 *                  pVddInstance/InstanceSwap
 *              else
 *                  return pload
 */
PVOID   PRIVENTRY vdmmGetFixup(pload,iType)
PVOID   pload;
ULONG   iType;
/*end*/
{

    if(ahp[iType].hp_fl & VDMAA_HT_FIXEDINSTANCE)
        // if allocation from fixed instance heap get fixup from there
        return((PVOID)((ULONG)pVDDFixedInstStart +
                      ((ULONG)pload - (ULONG)ahp[iType].hp_pv)));
    if(ahp[iType].hp_fl & VDMAA_HT_SWAPINSTANCE)
        // if allocation from Swap instance heap get fixup from there
        return((PVOID)((ULONG)pVDDSwapInstStart +
                      ((ULONG)pload - (ULONG)ahp[iType].hp_pv)));
    return pload;
}

/***LP+    heapAlloc - allocate region from heap
 *
 *      ENTRY   pheap - heap to allocate from
 *              cb - count of bytes to allocate
 *
 *      EXIT    SUCCESS, returns laddr assigned
 *              FAILURE, returns NULL
 */
PVOID   PRIVENTRY heapAlloc(pheap, cb)
PHEAP   pheap;
ULONG   cb;
/*end*/
{
register   PHEAPBLOCK phb;
PHEAPBLOCK phbNew,phbLast,phbNext;
ULONG      fl=0,cbCurrentHeapSize;
USHORT     flsel=0;
VMAC       vmac;
PVOID      laddr,p1,p3,p4;    /* p2, */

    cb = ALIGN_OBJECT(cb,sizeof(ULONG));          // Align dword
    phbLast = NULL;

    // Find free block that fits
    //      We loop until we find a free block that is large enough

    phb = pheap->hp_phb;                // get start of list
    while (phb != NULL && (!phb->hb_fFree ||
                            phb->hb_fFree && phb->hb_cb < cb)) {
        phbLast = phb;
        phb = phb->hb_phb;
    }

    // If we didn't find a free block, try to allocate a new one

    if (phb == NULL) {
        // make sure we can create another free block in the heap
        //      !VDMAA_HT_SELECTOR heaps start out with a free block
        //      the size of the entire heap, and hp_cbFreeHeapBlock
        //      is set to that size, too.  Thus, the following code
        //      will always fail for !VDMAA_HT_SELECTOR heaps.

        cbCurrentHeapSize = (ULONG)phbLast->hb_pv + phbLast->hb_cb
                 - (ULONG)pheap->hp_pv;

        if (cbCurrentHeapSize+(ULONG)pheap->hp_cbFreeHeapBlock
              > pheap->hp_cbMax)
            return NULL;                 // fail, heap is at maximum size

        // create another free block

        if((phb = heapAllocHB(cbHeapBlock)) == NULL)
            return NULL;
        phb->hb_pv = (PVOID)ROUND_UP(phbLast->hb_pv,cbHeapBlock);
        vdmmAssertEQ((ULONG)phb->hb_pv,
                       ((ULONG)phbLast->hb_pv + phbLast->hb_cb));

        phbLast->hb_phb = phb;

        // allocate fake selector (we only get this far for VDMAA_HT_SELCTOR
        //      heaps!)
        //
        //  NOTE:  For instance objects, we don't allocate selectors until
        //         VDM creation, since each VDM needs different selectors.
        //         To make that easier, we store "fake" selectors, starting
        //         from 0.  Then, at VDM_CREATE time we traverse the block
        //         list, using the changes in "fake" selector numbers to
        //         signal the need to create a new selector.
        //         For other heaps where selectors are required we fake
        //         the selector as zero. On commitiing the memory later this
        //         flag is selectors is tested for non-instance heaps.
        //         if it is found 0 memory is commited witha real selector
        //         allocation.

        if (pheap->hp_fl & VDMAA_HT_INSTANCE) {
            phb->hb_sel = phbLast->hb_sel+ (USHORT)1; // next fake selector
        }
        else if(pheap->hp_fl & VDMAA_HT_SELECTOR)
            phb->hb_sel = 0;
    }

    // Get block, splitting free block if necessary
    //      phb - free block to allocate from
    //      phbLast - block just prior to phb

    if (phb->hb_cb == cb) {             // free block is exact size desired
        phbNew = phb;
        phbNew->hb_fFree = FALSE;       // now in use
    }
    else {                              // have to split free block
        if((phbNew = heapAllocHB(cb)) == NULL)
            return NULL;

        if (phbLast == NULL)            // first !free block on list
            pheap->hp_phb = phbNew;     // have heap point to it
        else
            phbLast->hb_phb = phbNew;   // link in new block

        // Take address range from front of free block

        phbNew->hb_pv = phb->hb_pv;
        phbNew->hb_sel = phb->hb_sel;   // same selector as free block
        phbNew->hb_fFree = FALSE;       // now in use
        phbNew->hb_phb = phb;           // link to free block

        // adjust remaining free space

        phb->hb_pv = (PVOID)((ULONG)phb->hb_pv + cb); // advance address
        phb->hb_cb -= cb;               // reduce size
    }

    // Commit memory (This code is tricky)
    //
    //  We have to be very clever about committing just the right
    //  amount of pages, because the virtual memory manager will
    //  complain if we try to commit an already committed page.
    //
    //  phbLast = Block just prior to newly created block
    //              NULL if phbNew is first !free block on list
    //  phbNew  = Newly created block
    //  phb     = A free block from which phbNew is carved out.
    //            Note phb has to be there as cb was not equal
    //            to phb->hb_cb
    //  phbNext = Next block to phb. It has to be either NULL or
    //            or an allocated one. Two free blocks cannot be
    //            found consecutive.
    //
    //         P1   P2          P3          P4
    //  --------    --------    --------    --------
    //  |      |----|      |----|      |----|      |
    //  --------    --------    --------    --------
    //
    //    Last        New         phb         Next
    //
    //  0. cb = 0; (size to be committed is zero initially
    //
    // 00. if Last is NULL (New is at the head of list) laddr = P2; goto 3
    //
    //  1. If (ROUND_UP_PAGE(P1) >= P3) -> Go to 6 (Nothing to Commit)
    //
    //  2. Starting address of allocation (laddr) = (ROUND_UP_PAGE(P1))
    //
    //  3. If Next is NULL (end of list) -> cb = ROUND_UP_PAGE(P3)-laddr;Goto 7
    //
    //  4. If (ROUND_DOWN_PAGE(P4) <= ROUND_UP_PAGE(P3))
    //
    //    4a. If (laddr>=(ROUND_DOWN_PAGE (P4)) -> Go to 6 (Nothing to Commit)
    //
    //     5. Else Size to be committed (cb) = ROUND_DOWN_PAGE(P4) - laddr
    //
    //  5. Else size to be committed = (ROUND_UP_PAGE(P3) - laddr; Go to 7
    //
    //  6. return SUCCESS
    //
    //  7. Assert that cb is greater than or equal to a PAGESIZE and page
    //     granular.
    //
    //  8. VMSetMem(cb,laddr,..,..)
    //

    cb = 0;
    phbNext = phb->hb_phb;
    if(phbLast == NULL){
        vdmmAssertEQ(((ULONG)phbNew->hb_pv & 0xfff),0);
        laddr = phbNew->hb_pv;
    }
    else{
        p1 = (PVOID)ROUND_UP(phbNew->hb_pv,PAGESIZE);
        if(p1 >= phb->hb_pv)
            return phbNew->hb_pv;
        else
            laddr = p1;
    }
    p3 = (PVOID)ROUND_UP(phb->hb_pv,PAGESIZE);
    if(phbNext == NULL)
        cb = (ULONG)p3 - (ULONG)laddr;
    else{
        p4 = (PVOID)ROUND_DOWN(phbNext->hb_pv,PAGESIZE);
        if(p4 <= p3)
            if(laddr >= p4)
                return phbNew->hb_pv;
            else
                cb = (ULONG)p4 - (ULONG)laddr;
        else
            cb = (ULONG)p3 - (ULONG)laddr;
    }
    vdmmAssertGTE(cb,PAGESIZE);     // Commit at least a page
    vdmmAssertEQ((cb & 0xfff),0);     // Commit integral number of pages

    // If phbnew is the first block out of a newly created block
    // then for noninstance heaps create selectors also.
    if(!(pheap->hp_fl & VDMAA_HT_INSTANCE) &&
        (pheap->hp_fl & VDMAA_HT_SELECTOR)  &&
        (phbNew->hb_sel == 0)){
        fl = VMAC_SELALLOC;
        flsel = SELAL_W | SELAL_R;
    }

    // Arrange flags for VMSetMem.
    if(pheap->hp_fl &
          (VDMAA_HT_SWAPCODE | VDMAA_HT_SWAPDATA | VDMAA_HT_SWAPINSTANCE))
        fl |= VMS_COMMIT | PG_ZEROFILL | PG_SWAPPABLE | PG_R;
    else
        fl |= VMS_COMMIT | PG_ZEROFILL | PG_RESIDENT | PG_R;

    if(pheap->hp_fl &
          (VDMAA_HT_SWAPCODE | VDMAA_HT_INITCODE | VDMAA_HT_GLOBALCODE))
        fl |= PG_X;
    else
        fl |= PG_W;

    if(VMSetMem((ULONG)laddr,cb,HOBNULL,fl,NA) != NO_ERROR){
        phbNew->hb_fFree = TRUE;
        return NULL;
    }

    // If selectors were also allocated store tham in new and old
    // heap blocks.
    if(flsel)
        phbNew->hb_sel = phb->hb_sel = vmac.ac_sel;
    return phbNew->hb_pv;
}

/***EP+ VDMMDeassignAddress - Deassign linear address (see VDMMAssignAddress)
 *
 *      ENTRY   laddr - laddr assigned by VDMMAssignAddress
 *              flObj - Object flags
 *
 *      EXIT    SUCCESS, returns TRUE
 *              FAILURE, returns FALSE
 */

BOOL   EXPENTRY VDMMDeassignAddress(laddr,flObj)
PVOID  laddr;
ULONG  flObj;
/*end*/
{

    register      PHEAPBLOCK    phb;
    PHEAPBLOCK    phbLast,phbNext;
    PHEAP         pheap;
    PVOID         paddr,paddrPHB,pFreeStart,pFreeEnd;
    ULONG         iType;

    // Map Object flags to a heap
    iType = QueryObjType(flObj);
    if(iType == 0)
        return FALSE;

    iType -= 1;
    pheap = &ahp[iType];                // get heap

    phb = pheap->hp_phb;                // get first block
    phbLast = NULL;

    while (phb != NULL && phb->hb_pv != laddr) {
        phbLast = phb;
        phb = phb->hb_phb;
    }

    if (phb == NULL || phb->hb_fFree)   // laddr was not previously assigned
        return FALSE;

    // PTRB710437; Loader requirement. Make the object ZFILL if non-code
    if(!(iType == VDMAA_OT_INITCODE || iType == VDMAA_OT_GLOBALCODE ||
            iType == VDMAA_OT_SWAPGLOBALCODE))
        vdmmZeroFill(laddr,phb->hb_cb);

    // Mark the record free
    phb->hb_fFree = TRUE;

    // decommit any whole blocks that are free (tricky Code)
    //
    //  P1          P2     P3          P4
    //  --------    --------    --------
    //  |      |----|      |----|      |
    //  --------    --------    --------
    //  phbLast       phb        phbNext
    //
    //  p = ROUND_DOWN_PAGE(P2)
    //  if(p >= P1) =>  On left side nothing is there to free
    //      pFreeStart (Start address where to free) = ROUND_UP_PAGE(P2)
    //  else
    //      pFreeStart = p (some area can be freed on left side)
    //  p = ROUND_UP_PAGE(P3)
    //  if (p > P4) => Implies nothing can be freed on right side.
    //      pFreeEnd (End Address to free) = ROUND_DOWN_PAGE(P3)
    //  else
    //      pFreeEnd = p;
    //  if at least page to free (i.e pFreeEnd > pFreeStart)
    //  VMFreeMem(..,..)

    paddrPHB = (PVOID)ROUND_DOWN(phb->hb_pv,PAGESIZE);
    if(phbLast && phbLast->hb_fFree && phb->hb_sel == phbLast->hb_sel){
        if( paddrPHB >= phbLast->hb_pv)
            pFreeStart = paddrPHB;
        else
            pFreeStart = (PVOID)ROUND_UP(phb->hb_pv,PAGESIZE);
    }
    else
        pFreeStart = (PVOID)ROUND_UP(phb->hb_pv,PAGESIZE);

    paddr = (PVOID)((ULONG)(phb->hb_pv) + phb->hb_cb);
    paddrPHB = (PVOID)ROUND_UP(paddr,PAGESIZE);
    phbNext = phb->hb_phb;

    if(phbNext && phbNext->hb_fFree && phb->hb_sel == phbNext->hb_sel)
        if((ULONG)paddrPHB > (ULONG)phbNext->hb_pv + phbNext->hb_cb)
            pFreeEnd = (PVOID)ROUND_DOWN(paddr,PAGESIZE);
        else
            pFreeEnd = paddrPHB;
    else
       pFreeEnd = (PVOID)ROUND_DOWN(paddr,PAGESIZE);

    if(pFreeEnd > pFreeStart){
        vdmmAssertEQ((((ULONG)pFreeEnd - (ULONG)pFreeStart) & 0xfff),0)
        if(VMSetMem((ULONG)pFreeStart,
                  (ULONG)pFreeEnd - (ULONG)pFreeStart,
                  HOBNULL,VMS_DECOMMIT,NA) != NO_ERROR)
            return FALSE;
    }

    phb = heapCoalesceHB(phbLast,phb);  // coalesce last block with this block
    (VOID) heapCoalesceHB(phb,phb->hb_phb); // coalesce result with next block

    return TRUE;
}

/***LP+    heapFreeHB - Free a heap block
 *
 *      ENTRY   phb - block to free
 *
 *      EXIT    none
 */
BOOL   PRIVENTRY heapFreeHB(phb)
PHEAPBLOCK  phb;
/*end*/
{
    VMFreeKHB(VM_HKH_PUB_RESRW,phb);
    return TRUE;
}


/***LP+    heapCoalesceHB - try to coalesce two adjascent blocks
 *
 *      ENTRY   phb - first block
 *              phbNext - block pointed to by phb
 *
 *      EXIT    if either phb or phbNext is NULL, returns
 *                  value of non-NULL parameter;
 *              if blocks could be coalesced, returns
 *                  phb
 *              if blocks could NOT be coalesce, returns
 *                  phbNext
 */
PHEAPBLOCK PRIVENTRY heapCoalesceHB(phb,phbNext)
PHEAPBLOCK phb;
PHEAPBLOCK phbNext;
/*end*/
{

    if (phb == NULL)                    // at head of list
        return phbNext;                 //   no change to first entry

    if (phbNext == NULL)                // at end of list
        return phb;                     //   no change to last entry

    // Only coalesce if mapped by same selector (heaps that are
    //      not mapped by selectors have hb_sel == 0 always) and
    //      both blocks are free.

    if (phb->hb_sel == phbNext->hb_sel &&
        phb->hb_fFree &&
        phbNext->hb_fFree) {

        phb->hb_phb = phbNext->hb_phb;  // remove block from list
        phb->hb_cb += phbNext->hb_cb;   // add free space to previous block
        heapFreeHB(phbNext);            // free block
        return phb;                     // return resultant block
    }
    else
        return phbNext;                 // couldn't coalesce
}

/***LP+ heapAllocHB - allocate a heap block
 *
 *  ENTRY
 *      cb - block size
 *
 *  EXIT
 *      SUCCESS - returns heap block
 *      FAILURE - return NULL
 */
PHEAPBLOCK PRIVENTRY heapAllocHB(cb)
ULONG cb;
/*end*/
{
PHEAPBLOCK phb;

    if(VMAllocKHB(VM_HKH_PUB_RESRW,sizeof(HEAPBLOCK), (VMHOB)VDDBLOCKOWNER,NA,
            SSToDS(&phb)) != NO_ERROR)
        return NULL;
    phb->hb_fFree = TRUE;           // block is free
    phb->hb_cb = cb;
    phb->hb_phb = NULL;
    phb->hb_sel = NULL;             // allow !selector blocks to coalesce,
                                    //   and VDMAA_HT_INSTANCE blocks to
                                    //   have *fake* selectors at init time
    return phb;
}

#pragma END_MVDM_INIT_CODE


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

#define VMFLAGS1 VMAC_VDM |VMAC_LOCSPECIFIC
#define VMFLAGS2 VMAC_ARENAPRV
#define PGFLAGS  PG_W | PG_R

/***LP+ vdmmCopyMVDMInstance - Copy MVDM instance data
 *
 *      For newly created VDM copy the MVDM kernel instance data
 *
 *      ENTRY   fType
 *                  MM_FINSTANCE (Fixed Instance)
 *                  MM_SINSTANCE (Swappable instance)
 *
 *      EXIT    SUCCESS 1
 *              FAILURE 0
 *
 */
BOOL    PRIVENTRY vdmmCopyMVDMInstance(fType)
BOOL    fType;
/*end*/
{
PLR     plr;
VMAC    vmac;

    // Search for kernel instance record
    plr = laGlobalMap.la_plrAbove1MB;
    while(plr != NULL && !(plr->lr_fState & fType))
        plr = plr->lr_plr;

    // If not found return success (means that type of instance data
    // was not used)
    if(plr == NULL)
        return 1;

    // Allocate space and copy the instance data from global address space
    if(fType & MM_FINSTANCE) {
        vmac.ac_va = (ULONG) plr->lr_pv;
        if(VMAllocMem(plr->lr_cb, plr->lr_cb,
                   VMFLAGS1 | VMFLAGS2 | PGFLAGS | PG_RESIDENT, HOBNULL,
                   (VMHOB)VDDHEAPOWNER, HOBNULL, NA, NA, SSToDS(&vmac))
              != NO_ERROR)
            return 0;
        else
            VMCopy(cbKrnlInstance,pKrnlInstance,plr->lr_pv);
    }
    else {
        vmac.ac_va = (ULONG) plr->lr_pv;
        if(VMAllocMem(plr->lr_cb, plr->lr_cb,
                   VMFLAGS1 | VMFLAGS2 | PGFLAGS | PG_SWAPPABLE, HOBNULL,
                   (VMHOB)VDDHEAPOWNER, HOBNULL, NA, NA, SSToDS(&vmac))
              != NO_ERROR)
            return 0;
        else
            VMCopy(cbKrnlInstSwap,pKrnlInstSwap,plr->lr_pv);
    }
    return 1;
}

/***LP+ vdmmCopyVDDInstance - Copy VDD instance data
 *
 *      For newly created VDM copy the VDD instance data
 *
 *      ENTRY   pVDMAlias - VDM Alias address
 *              fType - MM_SINSTANCE
 *                      MM_FINSTANCE
 *              pheap - address where heap structure will be returned
 *
 *      EXIT    1 - Success
 *              0 - Fail
 *
 *      NOTE    if this routine fails any where there is no need to clean
 *              up memory from kernel heap.
 *              This code assumes that any VDD loading failure will
 *              close down the MVDM operation hence control will come
 *              only if all the VDDs were loaded successfully.
 *              In other words There can be only one free heap block at the
 *              end of a 64k block if there are more than one 64k blocks
 *              allocated. fixAssignAddress trims all the free
 *              blocks at the very end of the heap.
 *
 */

BOOL   PRIVENTRY vdmmCopyVDDInstance(pVDMAlias,fType,pheap)
PVOID  pVDMAlias;
BOOL   fType;
PHEAP  *pheap;
/*end*/
{
PLR     plr;
ULONG   cb;

    *pheap = NULL;

    if(fType == MM_FINSTANCE)
        cb = cbKrnlInstance;
    else
        cb = cbKrnlInstSwap;

    // Scan in the linear heap for a matching record.
    plr = laGlobalMap.la_plrAbove1MB;
    while(plr != NULL && !(plr->lr_fState & fType))
        plr = plr->lr_plr;

    // If not found means no allocation was made of fType
    if(plr == NULL)
        return 1;

    // Check if VDD has any allocations (It is possible that this record
    // represent allocation made by kernel instance only)
    else if(plr->lr_cb == cb)
        return 1;

    // Copy the global heap records and copy the instance data also. Local
    // copy of heap records is used by VDHQuerySel.
    if((*pheap = vdmmActualCopy(fType)) == NULL)
          return 0;

    if(vdmmCreateSel((*pheap)->hp_phb,
                     pVDMAlias,
                     plr->lr_pv,
                     plr->lr_cb) == 0){
         vdmmCVICleanup(*pheap);
         return 0;
    }
    return 1;
}
#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***LP+ vdmmActualCopy - Copy VDD instance data and create selector
 *
 *  ENTRY
 *      fType      -    Swap/Resident heap
 *
 *  EXIT
 *      SUCCESS - returns heap created
 *      FAILURE - NULL
 *
 *  PCODE
 *      Get the appropriate starting address where data will be copied as
 *          per fType
 *      Create the local heap head
 *      Copy each global heapo block to local heap. Starting address
 *          field will only get changed.
 *      if the block is free free the physical memory allocated in VDM's
 *          address space.
 *      else
 *          copy the VDD instance data from global memory
 *
 */
PHEAP   PRIVENTRY vdmmActualCopy(fType)
ULONG       fType;
/*end*/
{
register    PHEAPBLOCK  phbNew;
ULONG       fState;
PVOID       pInstStart;
PHEAP       php,phpGlobal;
PHEAPBLOCK  phbGlobal,phbLast=NULL,phb;
PVOID       pStart,pEnd;

    // Get the starting address of the VDD instance data
    if(fType == MM_SINSTANCE){
        pInstStart = pVDDSwapInstStart;
        fState = VDMAA_OT_SWAPINSTANCE;
    }
    else{
        fState = VDMAA_OT_FIXEDINSTANCE;
        pInstStart = pVDDFixedInstStart;
    }

    // Allocate heap head and copy from the global heap head
    if(VMAllocKHB(VM_HKH_PUB_RESRW,sizeof(HEAP), (VMHOB)VDDBLOCKHOWNER, NA,
                  SSToDS(&php)) != NO_ERROR)
        return NULL;

    // Copy heap head contents
    phpGlobal = &(ahp[fState]);
    phbGlobal = phpGlobal->hp_phb;
    php->hp_fl = phpGlobal->hp_fl;
    php->hp_pv = pInstStart;
    php->hp_cbMax = phpGlobal->hp_cbMax;
    php->hp_cbFreeHeapBlock = phpGlobal->hp_cbFreeHeapBlock;

    // Copy heap blocks
    while(phbGlobal != NULL){
        if(VMAllocKHB(VM_HKH_PUB_RESRW,sizeof(HEAPBLOCK), (VMHOB)VDDBLOCKOWNER,
                      NA, SSToDS(&phb)) != NO_ERROR){
            vdmmCVICleanup(php);
            return NULL;
        }
        phbNew = phb;
        phbNew->hb_pv = pInstStart;
        phbNew->hb_cb   = phbGlobal->hb_cb;
        phbNew->hb_sel  = phbGlobal->hb_sel;
        phbNew->hb_fFree= phbGlobal->hb_fFree;
        if(phbLast == NULL)
             php->hp_phb = phbNew;
        else
            phbLast->hb_phb = phbNew;
        if(phbNew->hb_fFree == FALSE)
            // block in use; copy the contents
            VMCopy(phbNew->hb_cb,phbGlobal->hb_pv,pInstStart);
        else{
            // Only  complete pages in this free block will be freed.
            pStart = (PVOID)ROUND_UP(phbNew->hb_pv,PAGESIZE);
            pEnd = (PVOID)ROUND_DOWN(((ULONG)phbNew->hb_pv + phbNew->hb_cb),
                              PAGESIZE);
            if(pEnd > pStart){
                if(VMSetMem((ULONG)pStart,
                            (ULONG)pEnd - (ULONG)pStart,
                            (VMHOB)VMS_DECOMMIT,
                            HOBNULL,NA) != NO_ERROR)
                    Panic(("vdmmActualCopy:VMFree"));
            }
        }
        pInstStart = (PVOID)((ULONG)pInstStart + phbNew->hb_cb);
        phbGlobal = phbGlobal->hb_phb;
        phbLast = phbNew;
    }
    if (phbNew)
        phbNew->hb_phb = NULL;
    return php;
}


/***LP+ vdmmCreateSel - Create selectors at every 64K and copy
 *                      selector in heap block records.
 *
 *  ENTRY
 *          phb = First heap block on the list.
 *          pVDMAlias = hvdm of the VDM being created.
 *          pStrat = address where instance data starts
 *          cb = size of instance data
 *
 *  EXIT
 *      SUCCESS - returns 1
 *      FAILURE - returns 0
 *
 *  PCODE
 *      Scan the list heap block
 *          At every 64k create a GDT selector
 *          Copy this selector in the blocks covering the 64k
 *
 */
BOOL    PRIVENTRY vdmmCreateSel(phb,pVDMAlias,pStart,cb)
PHEAPBLOCK  phb;
PVOID       pVDMAlias;
PVOID       pStart;
ULONG       cb;
/*end*/
{
SEL     selfake,sel;
ULONG   cbsel;

    if(phb == NULL)
        return 1;
    selfake = -1;   // sel in heap blocks start from 0

    while(phb != NULL){
        if(selfake != phb->hb_sel){
            // Create GDT selector in vdm alias
            sel =0;
            cbsel = (cb >= cbHeapBlock ? cbHeapBlock : cb);
            vdmmAssertGT(cbsel,0);
            if(SELLdrDesc((ULONG)pVDMAlias + (ULONG)pStart,
                          cbsel,
                          SSToDS(&sel)) != NO_ERROR)
                return 0;
            else{
                selfake = phb->hb_sel;
                cb = (cb <= cbsel ? 0 : cb - cbsel);
            }
        }
        phb->hb_sel = sel;
        phb = phb->hb_phb;
    }
    return 1;
}

#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***LP vdmmCVICleanup - Copying VDD instance data failed so free the
 *                      kernel heap memory.
 *
 *  ENTRY - php heap which was being created
 *
 *  EXIT  - None
 *
 *  PCODE
 *      Free each heap block (phb)
 *      Free heap head       (pheap)
 */
VOID    PRIVENTRY vdmmCVICleanup(php)
PHEAP   php;
{
PHEAPBLOCK  phb,phbNext;

    if(php == NULL)
        return;

    phb = php->hp_phb;
    while(phb != NULL){
        phbNext = phb->hb_phb;
        VMFreeKHB(VM_HKH_PUB_RESRW,phb);
        phb = phbNext;
    }
    VMFreeKHB(VM_HKH_PUB_RESRW,php);
    return;
}


/***LP vdmmZeroFill - Fill the object with zeros
 *
 *  ENTRY - laddr starting address
 *          cb    size
 *
 *  EXIT  - None
 *
 *  PCODE
 *          fill with zero with yielding in between if required
 */
VOID    PRIVENTRY vdmmZeroFill(laddr,cb)
PVOID   laddr;
ULONG   cb;
{

    while(cb){
        // After copying a page, yield CPU.
        if(cb >= PAGESIZE){
            memset(laddr,0,PAGESIZE);
            laddr = (PVOID)((ULONG)laddr + PAGESIZE);
            cb = cb - PAGESIZE;
            TKTCYield();
        }
        else{
            memset(laddr,0,cb);
            break;
        }
    }
    return;
}

ULONG   PRIVENTRY pfninfo(SGid,ulCmd,cbIn,pReqIn,cbOut,pReqOut)
SGID  SGid;
ULONG ulCmd;
ULONG cbIn;
PVOID pReqIn;
ULONG cbOut;
PVOID pReqOut;
{
INFOPACKET vddinfo;

    SGid;                     // eliminate compiler warnings
    ulCmd;
    cbIn;
    pReqIn;
    cbOut;

    // Collect the info
    vddinfo.ip_cbResCode    = ahp[VDMAA_OT_GLOBALCODE].hp_cbMax;
    vddinfo.ip_cbSwapCode   = ahp[VDMAA_OT_SWAPGLOBALCODE].hp_cbMax;
    vddinfo.ip_cbInitCode   = ahp[VDMAA_OT_INITCODE].hp_cbMax;
    vddinfo.ip_cbResData    = ahp[VDMAA_OT_GLOBALDATA].hp_cbMax;
    vddinfo.ip_cbSwapData   = ahp[VDMAA_OT_SWAPGLOBALDATA].hp_cbMax;
    vddinfo.ip_cbInitData   = ahp[VDMAA_OT_INITDATA].hp_cbMax;
    vddinfo.ip_cbResInst    = cbKrnlInstance +
                                  ahp[VDMAA_OT_FIXEDINSTANCE].hp_cbMax;
    vddinfo.ip_cbSwapInst   = cbKrnlInstSwap +
                                  ahp[VDMAA_OT_SWAPINSTANCE].hp_cbMax;

    // Store in user area
    TKSuBuff(pReqOut,SSToDS(&vddinfo),sizeof(INFOPACKET),0);

    return 0;
}

#pragma END_MVDM_SWAP_CODE
