#define SCCSID "@(#)vdmmcrt.c   6.9 92/03/20"

/***    VDMMCRT.C
 *
 *      IBM Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1992
 *
 *      All Rights Reserved
 *
 *      Title: VDM Creation Exported Procedures
 *
 *      Description: Dos API to create a VDM
 *
 *      Modification History:
 *
 * 6.1  10/14/88                            Created
 * 6.4  06/28/91 CP20 B724598 Jeff Muir     Only mark VDM created when no error
 * 6.5  07/12/91 CP20 DCR1371 Mark Rogalski Add support for FSFILTER.
 * 6.7  09/15/91 CP20 B726841 Jeff Muir     Delete IOPL property
 * 6.8  02/06/92 CP20 B732228 Stacey Barnes Add new state for mi_fState
 * 6.9  03/20/92 CP20 B734858 Stacey Barnes Free memory from vdmqueryproperty
 * 1.2  09/15/92 r205 p053350 Jeff Muir     EMS Low mem region > 384K
;---------------------------------------------------------------------- ;58348;
; 93Feb18 - Fulkgl                                                      ;58348;
; Add a new parameter to DosCreateVDM to pass current drive and sub-    ;58348;
; directory information to the new DOS session.                         ;58348;
;---------------------------------------------------------------------- ;58348;
; 93Jul13 - Fulkgl                                                      ;70592;
; Move the changing of current drive and sub-directory to after the     ;70592;
; creation of a new process.  Otherwise we change Session Manager's.    ;70592;
;----------------------------------------------------------------------
; 94Sep19 - bjn
; Changed segment names for page tuning.
;----------------------------------------------------------------------
 */
#define FNO_NUM  FNO_VDMMCRT_C
#include <fnomvdm.h>

#define INCL_NONE
#define INCL_TYPES
#define INCL_MVDMTYPES
#define INCL_VDHALL
#define VDDSTRICT

#include <vdmvdh.h>
#include <limits.h>
#include <kmode.h>
#include <kutil.h>
#include <propname.h>
#ifdef SECURITY
   #include <secure.h>   //@SEC
   #include <secure2.h>
#endif

#define MAX_FAILURE_CAUSE       256

extern  HVDM            vdmid_HVDM;
extern  HLA             vdmid_hla;
extern  ULONG           vdmid_VDMSIG;
extern  LSYSVAL         vdmid_aLSV[];
extern  ULONG           vdmid_RMSIZE;
extern  ULONG           vdmid_ulFreezeCnt;
extern  PHEAP           vdmid_phpFixed;
extern  PHEAP           vdmid_phpSwap;
extern  PTCB            vdmid_mphvdmptcb;
extern  PPTDA           vdmid_mphvdmpptda;
extern  HPFH            vdmid_hpfh;
extern  MAPID           vdmid_mi;
extern  BYTEHEAP        vdmid_DosHeap;
extern  BOOL            vdmid_fFgnd;
extern  PFN             vdmid_apfnUndo[];
extern  ULONG           vdmid_flVDM;

extern  LA              laGlobalMap;
extern  BOOL            fIsMVDMPresent;
extern  ULONG           nVDMCreated;
extern  EH              aeh[];
extern  PVDDEP          pvddep;
extern  HMAPID          hmapid;
extern  ULONG           aVDMMSysVal[];
extern  CRI             criVSF;
extern  struct          infoseg_s Lis2;
extern  SEL             selInstanceData;
extern  struct InfoSegGDT SISData;
extern  LSYSVAL         vdmid_aLSV[];

extern  VOID            vdmmStart(VOID);
/*extern VOID PASCAL    demSetCDS(PVOID);                               70592*/
extern  INT PASCAL      vdmmEnterCreateVDM(VOID);
extern  INT PASCAL      vdmmExitCreateVDM(INT, F16PVOID, RESULTCODES);
extern  VOID PRIVENTRY  vdmmProcFBgnd(USHORT, BOOL);
extern  BOOL            PRIVENTRY vdmmCopyVDDInstance(HVDM,BOOL,PVOID);
extern  BOOL            PRIVENTRY vdmmCopyMVDMInstance(BOOL);
extern  HLA             PRIVENTRY LHCopyLA(HLA);
extern  VOID            PRIVENTRY pfnUndoLA();
extern  VOID            PRIVENTRY pfnUndoEM86();
extern  VOID            PRIVENTRY pfnUndoVDD();
extern  VOID            PRIVENTRY pfnUndoHMAPID();
extern  VOID            PRIVENTRY pfnUndoFixedHeap();
extern  VOID            PRIVENTRY pfnUndoSwapHeap();
extern  VOID            PRIVENTRY vdmmDisarmTimerHooks(VOID);
extern  VOID            PRIVENTRY vdmmProcCreateDone(VOID);

BOOL    PRIVENTRY       vdmmFillHoles(ULONG);
BOOL    PRIVENTRY       vdmmInitInstance(VOID);
VOID    PRIVENTRY       vdmmInitMapid(VOID);
VOID    PRIVENTRY       vdmmMarkVDM(VOID);
VOID    PRIVENTRY       vdmmInitDosHeap(ULONG);
INT     PRIVENTRY       vdmmCopyProperties(F16PVOID, HPROPINST *);
INT     PRIVENTRY       vdmmStrDup(F16PVOID, PVOID *, BOOL, BOOL);
INT     PRIVENTRY       vdmmAllocTempBlock(ULONG, PVOID *);
VOID    PRIVENTRY       vdmmFreeTempBlocks(VOID);
VOID    PRIVENTRY       vdmmInitUndoList(VOID);
VOID    PRIVENTRY       vdmmAddUndoItem(PFN);
VOID    PRIVENTRY       vdmmVDMCreated(VOID);


#pragma BEGIN_MVDM_SWAP_DATA

// Following three are linear addresses of the copies of the corresponding
// 16:16 pointers passed from DosCreateVDM API.  As one VDM can be created
// at a time one set of global variables will do the job.

HPROPINST       hprinCreateVDM;
PSZ             pszDemCurrDir;                                          //70592
PSZ             pszProgCreateVDM;
PSZ             pszArgsCreateVDM;
CHAR            szFailureCause[MAX_FAILURE_CAUSE];
SZ              szEMSLowMem = PROP_VEMM_LOW;

#pragma END_MVDM_SWAP_DATA


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

/***LP+ VDMMApiCreateVDM - Dos API worker to create a VDM
 *
 *      OS/2 API to create a VDM session (restricted to use by the
 *      Session Manager only).
 *
 *      ENTRY
 *          f16pErrorBuffer     - pointer to error buffer
 *          cbErrorBuffer       - number of bytes in error buffer
 *          f16pszArgs          - pointer to VDM program arguments
 *          f16ppropbuf         - pointer to VDM property buffer
 *          f16presultcodes     - return code
 *          f16pszProg          - pointer to VDM program filename to run under
 *                                the VDM shell;  can be a COM, EXE, or BAT
 *                                filename;if NULL, the new session will wait
 *                                at the VDM shell's (ie, COMMAND.COM) prompt
 *          ulsgid              - screen group # assigned by the Session Manager
 *          f16pdemcds          - Current Directory Structure information 58348
 *
 *      EXIT
 *          SUCCESS
 *              0
 *          FAILURE
 *              !0: ERROR_VDM_DOWN
 *                  ERROR_INVALID_CALLER
 *                  ERROR_NOT_ENOUGH_MEMORY
 *
 *      CONTEXT
 *          OS/2 protected-mode process
 *
 *      INVOKE
 *          push    DWORD f16pErrorBuffer
 *          push    DWORD cbErrorBuffer
 *          push    DWORD f16pszArgs
 *          push    DWORD f16ppropbuf
 *          push    DWORD f16presultcodes
 *          push    DWORD f16pszProg
 *          push    DWORD ulsgid
 *          push    DWORD f16pdemcds                                    ;58348;
 *          call    DOSCREATEVDM
 *
 *      PSEUDO-CODE
 *          If VDM support is non-operational
 *              return ERROR_VDM_DOWN
 *          If caller is not the Shell/Session Manager
 *              return ERROR_aINVALID_CALLER
 *          grab the create VDM mutex semaphore (vdmmEnterCreateVDM)
 *          create a PTDA  (TKCreatePTDA)
 *          fill VDM fields and mark the process type as VDM in Local infoseg
 *          create the rest of the VDM process (TKCreateProcess)
 *          wait for vdmmCreateVDM to finish (vdmmExitCreateVDM)
 *          return result codes to shell and clear create VDM mutex semaphore
 *          return
 */

INT CDECL
VDMMApiCreateVDM(sef, pdemcds, ulsgid, f16pszProg, f16presultcodes,
                 f16ppropbuf, f16pszArgs, cbErrorBuffer, f16pErrorBuffer)
SEF      sef;
PVOID    pdemcds;                                                     /*58348*/
ULONG    ulsgid;
F16PVOID f16pszProg;
F16PVOID f16presultcodes;
F16PVOID f16ppropbuf;
F16PVOID f16pszArgs;
ULONG    cbErrorBuffer;
F16PVOID f16pErrorBuffer;
/*end*/
{
    PVOID p;
    register INT rc;
    RESULTCODES resultcodes;

    sef;                      // eliminate compiler warning

    // Check if MVDM is in Operation
    if(ISMVDMPRESENT == 0) {
        return(ERROR_VDM_DOWN);
    }

    // Grab the create VDM mutex semaphore
    if((rc = vdmmEnterCreateVDM()) != NO_ERROR) {
        return(rc);
    }

    pszDemCurrDir = pdemcds;                                            //70592

    // Copy the args field in global memory to be accessible in
    // vdmmCreateVDM. Only one VDM can be created at a time, so
    // using one set of global variables is safe.

    if (!(rc=vdmmCopyProperties(f16ppropbuf, &hprinCreateVDM)) &&       //70592
        !(rc=vdmmStrDup(f16pszProg, &pszProgCreateVDM, FALSE,FALSE)) && //70592
        !(rc=vdmmStrDup(f16pszArgs, &pszArgsCreateVDM, FALSE,FALSE)) )  //70592
    {                                                                   //70592
        #ifdef SECURITY
           if (fSec) {  //@SEC
              // Ask Security if this is OK
              rc = SecCreateVDM(pszProgCreateVDM, pszArgsCreateVDM);
           }
        #endif
        if (!rc) {
           // Create the VDM process
           rc = TKCreateVDMProcess(vdmmStart, (USHORT)ulsgid,SSToDS(&resultcodes));
        }
        #ifdef SECURITY
           if (fSec) {
              SecCreateVDMPost(rc);
           }
        #endif

    }                                                                   //70592

    // Copy fail cause to return buffer
    if (rc != NO_ERROR && f16pErrorBuffer != NULL && cbErrorBuffer != NULL)
    {
        if ((p = F16PTOLIN(f16pErrorBuffer)) == (PVOID)-1)
        {
            TKUsrMemGPFault(SEGMENTOF16(f16pErrorBuffer));
        }
        else
        {
            TKSuBuffz(NULL, p, szFailureCause, cbErrorBuffer, TK_UM_FATAL);
        }
    }

    // return result codes to shell, wait for vdmmCreateVDM to finish and
    // release the create VDM mutex semaphore
    return(vdmmExitCreateVDM(rc, f16presultcodes, resultcodes));
}

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

/***LP+ vdmmCreateVDM - in-context create vdm routine
 *
 *      This routine is called by vdmmStart in the context of
 *      of a new VDM.  The VDM creation finished, 8086 emulation
 *      and DOS emulation creation entry points are called.
 *
 *      ENTRY
 *          NONE
 *
 *      EXIT
 *          SUCCESS
 *              0
 *          FAILURE
 *              !0
 *
 *      CONTEXT
 *          VDM Task
 *
 *      NOTE
 *          This routine creates the initial memory map of the VDM task as
 *          globally specified to VDMMInit during System Initialization.
 *          The initial map may be updated because of different VDD
 *          configuration values for different VDMs.
 *
 *          In case of any error in the whole process, the VDM creation is
 *          stopped and depending on the error, the cleaning up of resources
 *          is started.
 *
 *      PSEUDO-CODE
 *          Mark the time as VDMCreateTime
 *          Copy VDM global memory map to Local memory map
 *          Initialize VDMM instance data i.e. Errorcode,hla,ptda,hpfh etc.
 *          Store PID,SGid and HVDM in MAPID structure
 *          (After this VDM creation takes place in this new VDM Context)
 *          Call 8086 emulation
 *          From aeh table call VDD's creation entry points
 *          Allocate the holes in the MemMap below 640k
 *          Call DEMCreateVDM(pszProg,pszArgs,pArena,aenasize,
 *            MAX_FAILURE_CAUSE, szFailureCause)
 *          Mark CreateTime is over
 *          return
 *
 *      NOTE
 *          VDM Creation failure is handled as follows:
 *          There are two possibilities to handle in case VDM craetion fails.
 *          One when the process is not marked as VDM. In this case
 *          VDMMKillVDM will not get control when this process will be killed.
 *          So process will not be marked as a VDM till some work has
 *          been done which has to be cleaned by VDMMKillVDM. Once process
 *          is marked as VDM everything which need to be cleaned at
 *          termination will be placed on an array in reverse order.
 *          So if VDM creation fails and the process was marked a VDM
 *          VDMMKillVDM will cleanup those work items which were done
 *          in VDM creation by enumerating the undo array.
 *          if VDM was created successfully all the undo items will
 *          remain in undo array and when this VDM will die
 *          VDMMKillVDM will do the same job as it does for those
 *          VDMs for which creation fails.
 */

INT PASCAL
vdmmCreateVDM()
{
INT             rc;
ULONG           ulDosArena;
PEL             pel;
PSZ             pszVMBoot=NULL; // 6.9
ULONG           ulEMSLowMem;
ULONG           nLowPages;

    // Copy Kernel instance data (both fixed and swap)
    if(vdmmCopyMVDMInstance(MM_FINSTANCE) == FALSE)
        return ERROR_NOT_ENOUGH_MEMORY;

    if(vdmmCopyMVDMInstance(MM_SINSTANCE) == FALSE)
        return ERROR_NOT_ENOUGH_MEMORY;

    // Initialize undo list
    vdmmInitUndoList();

    // Set property instance
    vdmSetPropertyInstance(hprinCreateVDM);

    // Create VDM Alias.
    if(PGInitVDMContext(pPTDACur, (PULONG)(&vdmid_HVDM)) != 0)
        return ERROR_NOT_ENOUGH_MEMORY;

    // Initialize vdm Manager instance data
    if(vdmmInitInstance() == 0)
        return ERROR_NOT_ENOUGH_MEMORY;
    else
        // Add HMAPID allocated undo item
        vdmmAddUndoItem(&pfnUndoHMAPID);

    // mark process as a VDM. After this if the VDM creation will fail
    // VDMMKillVDM will be called.
    vdmmMarkVDM ();

    // Increment the number of VDMs in system. This count is getting
    // incremented here because if VDM fails in creation it has to be handled
    // by VDMMKillVDM.
    nVDMCreated++;

    // Sign the VDM
    vdmid_VDMSIG = HVDM_SIGNATURE;

    // Copy VDM Global Memory Map to this new VDM local map
    if((vdmid_hla = LHCopyLA(&laGlobalMap)) == NULL){
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    else
        // Add LA copied undo item
        vdmmAddUndoItem(&pfnUndoLA);

    // Copy VDD swap instance data
    if(vdmmCopyVDDInstance(vdmid_HVDM,MM_SINSTANCE,&vdmid_phpSwap)
                                    == 0){
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    else
        // Add VDD swap Heap created undo item
        vdmmAddUndoItem(&pfnUndoSwapHeap);

    // Copy VDD fixed instance data
    if(vdmmCopyVDDInstance(vdmid_HVDM,
                           MM_FINSTANCE,&vdmid_phpFixed) == 0){
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    else
        // Add VDD fixed Heap created undo item
        vdmmAddUndoItem(&pfnUndoFixedHeap);

    // 1.2 r205 In order for EMS to claim greater than 384K of
    // memory in the conventional address space, VDMM needs
    // to decrease reserved space for the DOS heap from 256K to
    // 64K.  This increases the window for EMS.  We still support
    // 256K for compatibility in VDDs using VDHAllocDosMem.

    ulEMSLowMem = (ULONG)VDHQueryProperty(szEMSLowMem);
    if(ulEMSLowMem>384)
      nLowPages = 16;                 /* 64K  */
    else
      nLowPages = 64;                 /* 256K */

    // Reserve and allocate the low dos arena memory;
    // Make record in linear heap also.
    if( (!VDHReservePages((PVOID)0,(ULONG)nLowPages)) ||
        !VDHAllocPages((PVOID)0,nLowPages,VDHAP_SPECIFIC))
        return ERROR_NOT_ENOUGH_MEMORY;

    //Initialize DosByte Heap
    vdmmInitDosHeap((ULONG)nLowPages*4096L);

    pszVMBoot = (PSZ)vdmQueryProperty(VDMP_ORD_VMBOOT);
    if (pszVMBoot)  {
      if (*pszVMBoot)
        vdmid_flVDM |= VDM_VMBOOT;
      VMFreeKHB(VM_HKH_PUB_SWAPRW, pszVMBoot);
      }

    // Call 8086 emulation creation
    if((rc = EM86CreateVDM()) == NO_ERROR) {

        // Add EM86CreateVDM undo item
        vdmmAddUndoItem(&pfnUndoEM86);

/*BD009    if (!(vdmid_flVDM & VDM_VMBOOT)) {                 */
            if ((rc = DEMCreateVDM1()) == NO_ERROR) {
                // Add DEM creation handler called undo item
                vdmmAddUndoItem(&DEMDestroyVDM1);
            }
/*BD009    }                                                  */
        if (rc == NO_ERROR) {
            // Add VDDs Creation handler called undo item
            vdmmAddUndoItem(&pfnUndoVDD);

            // Add timer hook termination handler
            vdmmAddUndoItem((PFN)&vdmmDisarmTimerHooks);

            // call VDDs creation entry points
            pel = aeh[VDM_CREATE].eh_pelFirst;
            while (pel != NULL) {
                if (!(*pel->el_pfn)(vdmid_HVDM)) {
                    // BUGBUG -- Are we going to require the VDDs to call
                    //           VDHSetError, or assume problem is memory? -JTP
                    rc = ERROR_NOT_ENOUGH_MEMORY;
                }
                pel = pel->el_pel;
            }
            if (rc == NO_ERROR) {
                vdmid_mi.mi_fState = VDM_AFTER_CREATE_TIME; // 6.8
                // call VDDs creation done entry points
                pel = aeh[VDM_VDD_CREATE_DONE].eh_pelFirst;
                while(pel != NULL) {
                    (*pel->el_pfn)(vdmid_HVDM);
                    pel = pel->el_pel;
                }

                // call VDD foreground/background entry points
                vdmmProcFBgnd((USHORT)vdmid_mi.mi_SGid,
                  vdmid_mi.mi_SGid != SISData.SIS_CurScrnGrp);

                // Fill holes in Dos Arena
                if(vdmmFillHoles(vdmid_RMSIZE * _1K) == FALSE){
                    return ERROR_NOT_ENOUGH_MEMORY;
                }
                if (!(vdmid_flVDM & VDM_VMBOOT)) {
                    // Call DEMCreateVDM2 with size of dos arena and its starting addr
                    ulDosArena = (vdmid_RMSIZE * _1K) - (ULONG)vdmid_DosHeap.bh_pCur;
                    if((rc = DEMCreateVDM2(pszProgCreateVDM, pszArgsCreateVDM,
                        (PBYTE) vdmid_DosHeap.bh_pCur,
                        ulDosArena, MAX_FAILURE_CAUSE, szFailureCause)) == NO_ERROR)

                        // Add Dem Creation handler called undo item
                        vdmmAddUndoItem(&DEMDestroyVDM2);
                }
            }
        }
    }

    // Free the temporary blocks allocated with vdmmAllocTempBlock
    vdmmFreeTempBlocks();
    hprinCreateVDM = NULL;
    pszProgCreateVDM = NULL;
    pszArgsCreateVDM = NULL;

    // Process VDM creation completed successfully notifications
    if (rc == NO_ERROR)
    {
       // Mark that VDM created
       vdmmVDMCreated();                       /* 6.4 */
       vdmmProcCreateDone();
    }

    // VDMCreateTime is over
    return(rc);
}
#pragma code_seg()
#pragma code_seg ("SWAP32_23","CODE")
/***LP+ vdmmInitInstance - initialize VDM manager's instance data
 *
 *  ENTRY
 *      None
 *  EXIT
 *      None
 */
BOOL    PRIVENTRY vdmmInitInstance()
/*end*/
{

    vdmid_RMSIZE = vdmQueryProperty(VDMP_ORD_RMSIZE);
    vdmid_ulFreezeCnt   = 0;                        // freeze count is zero
    vdmid_mphvdmptcb    = pTCBCur;                  // Current TCB
    vdmid_mphvdmpptda   = pPTDACur;                 // Current PTDA

    // Initialize remaining Local sys values of VDHQuerySysValue
    vdmmAssertEQ(LSVSTART,VDHLSV_HVDM);
    vdmid_aLSV[VDHLSV_PRIORITYCLASS-LSVSTART].lsv_pv = &pTCBCur->TCBPriClass;
    vdmid_aLSV[VDHLSV_PRIORITYLEVEL-LSVSTART].lsv_pv = &pTCBCur->TCBPriLevel;
    vdmid_aLSV[VDHLSV_PCRF-LSVSTART].lsv_pv = &pTCBCur->TCB_pFrameBase;
    vdmid_aLSV[VDHLSV_CODEPAGEID-LSVSTART].lsv_pv = &pPTDACur->codepage_tag;

    // Initialize page fault handling
    vdmid_hpfh.hf_ppfhFirst   = vdmid_hpfh.hf_ppfhLast = NULL;

    // Initialize MAPID
    vdmmInitMapid ();

    return 1;
}

#pragma code_seg ("SWAP32_23","CODE")
/***LP+ vdmmCopyProperties - Create property instance copy from f16 buffer
 *
 *  Parse PROPERTYBUFFER and create a property instance.
 *
 *  ENTRY
 *      f16p
 *          16:16 address of PROPERTYBUFFER
 *
 *      ppprin
 *          variable to receive PROPINST handle.
 *
 *  EXIT
 *      SUCCESS
 *          NO_ERROR
 *      FAILURE
 *          error code
 */

INT PRIVENTRY vdmmCopyProperties(f16p, phprin)
F16PVOID f16p;
HPROPINST *phprin;
/*end*/
{
    PPROPERTYBUFFER ppropbuf;

    // Nothing to check if NULL pointer
    if(f16p == NULL) {
        ppropbuf = NULL;
    }
    else {
        // convert to linear pointer
        ppropbuf = (PPROPERTYBUFFER)F16PTOLIN(f16p);
        if ((LONG)ppropbuf == -1) {          // pointer was bad
            TKUsrMemGPFault(SEGMENTOF16(f16p));
            return ERROR_PROTECTION_VIOLATION;
        }
    }
    // create property instance
    return vdmCreatePropertyInstance(ppropbuf,phprin);
}


#pragma code_seg()
#pragma code_seg ("SWAP32_23","CODE")
/***LP+ vdmmStrDup - duplicate psz or pszz string from a user's 16:16 pointer
 *
 *  ENTRY
 *      f16p
 *          16:16 address of string
 *      pp
 *          linear pointer to put copied string
 *      fPSZZ
 *          if TRUE, the string is a pszz string
 *      fPermanent
 *          if TRUE, the string isn't freed after VDM creation is
 *          finished.  It is left to the call to free the block when
 *          it is no longer needed.
 *
 *  EXIT
 *      SUCCESS
 *          NO_ERROR
 *      FAILURE
 *          error code
 *
 *  NOTE
 *      This routine must return a NULL pointer (pp) if the string is
 *      zero length string.
 *
 */

INT PRIVENTRY vdmmStrDup(f16p, pp, fPSZZ, fPermanent)
F16PVOID f16p;
PVOID *pp;
BOOL fPSZZ, fPermanent;
/*end*/
{
    register INT rc;
    ULONG cb;
    PVOID p;

    if(f16p == NULL) {
        *pp = NULL;
        return(NO_ERROR);
    }
    if((p = F16PTOLIN(f16p)) == (PVOID)-1) {
        TKUsrMemGPFault(SEGMENTOF16(f16p));
        return(ERROR_PROTECTION_VIOLATION);
    }
    if((rc = TKFuBufLen(SSToDS(&cb), p, _1K, TK_UM_FATAL, fPSZZ)) != NO_ERROR) {
        return(rc);
    }
    if(cb == 0 || (!fPSZZ && ((cb - 1) == 0)) || (fPSZZ && ((cb - 2) == 0))) {
        *pp = NULL;
        return(NO_ERROR);
    }
    if(fPermanent) {
        rc = VMAllocKHB(VM_HKH_PUB_RESRW, cb, (VMHOB)VDDSTROWNER, NA, pp);
    }
    else {
        rc = vdmmAllocTempBlock(cb, pp);
    }
    if(rc != NO_ERROR) {
        return(rc);
    }
    return(TKFuBuff(*pp, p, cb, TK_UM_FATAL));
}

#pragma code_seg()
#pragma code_seg ("SWAP32_26","CODE")
/***LP+ vdmmAllocTempBlock - allocate temporary blocks
 *
 *  Allocates a temporary block of memory freed when VDM
 *  creation is over.
 *
 *  ENTRY
 *      cb
 *          size of block
 *      pp
 *          pointer to put address of block
 *
 *  EXIT
 *      SUCCESS
 *          NO_ERROR
 *      FAILURE
 *          error code
 */

PVOID *pTempBlockHead = NULL;            // head of vdmmAllocTempBlock list

INT PRIVENTRY vdmmAllocTempBlock(cb, pp)
ULONG cb;
PVOID *pp;
/*end*/
{
    INT rc;

    if((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, cb + sizeof(PVOID),
                        (VMHOB)VDDCREATMPOWNER, NA, pp)) == NO_ERROR) {
        **(PVOID **)pp = pTempBlockHead; // set the blk's link to head of list
        pTempBlockHead = (PVOID *) *pp;  // set head of list to the new block
        (*(PVOID **)pp)++;               // bump block address past link ptr
    }
    return(rc);
}
#pragma code_seg ("SWAP32_23","CODE")
/***LP+ vdmmFreeTempBlocks - frees temporary blocks
 *
 *  Frees all the temporary blocks of memory allocated with
 *  vdmmAllocTempBlock.
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      None
 */

VOID PRIVENTRY vdmmFreeTempBlocks()
/*end*/
{
    register PVOID p;

    while(pTempBlockHead != NULL) {
        p = *pTempBlockHead;
        VMFreeKHB(VM_HKH_PUB_SWAPRW, pTempBlockHead);
        pTempBlockHead = (PVOID *) p;
    }
}

#pragma code_seg()
#pragma code_seg ("SWAP32_23","CODE")
/***LP+ vdmmInitDosHeap - Initialize Dos heap
 *
 *  Initialize Dos Heap
 *
 *  ENTRY
 *      None
 *  EXIT
 *      None
 */
VOID    PRIVENTRY vdmmInitDosHeap(ULONG ulHeapMem)
/*end*/
{

    vdmid_DosHeap.bh_pv = NULL;
    vdmid_DosHeap.bh_pCur = NULL;
/*    vdmid_DosHeap.bh_cb = DOSMEMSIZE; */
    vdmid_DosHeap.bh_cb = ulHeapMem;
    return;
}
#pragma code_seg()
#pragma code_seg ("SWAP32_23","CODE")
/***LP+ vdmmFillHoles - fill the holes in dos arena
 *
 *  VDM Creation is completing. So this routine is called to allocate
 *  memory wherever the holes have been left in dos arena.
 *
 *  ENTRY
 *      rmsize - Dos arena size
 *
 *  EXIT
 *      SUCCESS
 *          returns TRUE
 *
 *      FAILURE
 *          returns FALSE (not enough memory. VDM cant be created)
 *
 *  CONTEXT
 *      VDM Creation
 *
 *  PCODE
 *      Fill holes till current record is totally inside rmsize
 *        if next record is also starting below rmsize
 *          hole size to allocate is start add. of next record - end add of cur
 *        else
 *          hole size is rmsize - end addr of current record
 *        if hole size is > 0 allocate memory
 *        if allocation fails return FLASE
 *      return TRUE
 *
 *      NOTE
 *          if this routine returns FALSE caller should undo all the
 *          VDM creation done so far.
 */

BOOL PRIVENTRY vdmmFillHoles(rmsize)
ULONG   rmsize;
/*end*/
{
PLR     plrCur,plrNext;
ULONG   ulSize;

    plrCur = vdmid_hla->la_plrFirst;

    //Fill holes if the current record is totaly inside rmsize
    while(plrCur != NULL && (ULONG)plrCur->lr_pv < rmsize
                         && (ULONG)END_ADDR(plrCur) < rmsize){
        plrNext = plrCur->lr_plr;
        if(plrNext != NULL && (ULONG) plrNext->lr_pv < rmsize)
            ulSize = (ULONG)plrNext->lr_pv - (ULONG)END_ADDR(plrCur);
        else
            ulSize = rmsize - (ULONG)END_ADDR(plrCur);

        if(ulSize){
            if(!VDHAllocPages(END_ADDR(plrCur),
                              (ulSize + PAGESIZE - 1)/PAGESIZE,
                              VDHAP_SPECIFIC))
                return FALSE;
        }
        plrCur = plrNext;
    }
    return TRUE;
}
#pragma code_seg()
#pragma code_seg ("SWAP32_23","CODE")
/***LP+ vdmmInitUndoList - Initialize undo list of VDM Creation
 *
 *  ENTRY
 *      None
 *  EXIT
 *      None
 */
VOID    PRIVENTRY   vdmmInitUndoList()
/*end*/
{
ULONG   i;

    for(i = 0; i < CREATE_UNDO_ITEMS; i++)
        vdmid_apfnUndo[i] = NULL;
    return;
}
#pragma code_seg()
#pragma code_seg ("SWAP32_23","CODE")
/***LP+ vdmmAddUndoItem - Add an undo item on VDM Creation undo list
 *
 *  ENTRY
 *      pfnUndo - undo function to be added
 *
 *  EXIT
 *      None
 *
 *  NOTE
 *      These functions are operated in LIFO order.
 */
VOID    PRIVENTRY   vdmmAddUndoItem(pfnUndo)
PFN     pfnUndo;
/*end*/
{
INT i;

    if(pfnUndo == NULL)
        return;

    // Put this function in the array at first vacant slot from the high end
    for(i = CREATE_UNDO_ITEMS - 1; i >= 0; i--){
        if(vdmid_apfnUndo[i] == NULL){
            vdmid_apfnUndo[i] = pfnUndo;
            return;
        }
    }
    Panic(("vdmmAddUndoItem: too many functions"));
}

#pragma code_seg()
#pragma BEGIN_MVDM_GLOBAL_CODE

/***LP+ vdmmInitMapid - Initialize and link MAPID structure for
 *                      the VDM being created.
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      None
 *
 */
VOID    PRIVENTRY   vdmmInitMapid()
/*end*/
{
PMAPID          pmi;

    // Initialize MAPID for this VDM
    vdmid_mi.mi_Pid     = pPTDACur->ptda_infoseg.LIS_CurProcID;
    vdmid_mi.mi_SGid    = pPTDACur->ptda_infoseg.LIS_CurScrnGrp;
    vdmid_mi.mi_hvdm    = vdmid_HVDM;
    vdmid_mi.mi_fState  = VDM_BEING_CREATED;
    vdmid_mi.mi_pmi     = NULL;
    pmi = (PMAPID)((ULONG)vdmid_HVDM + (ULONG)&vdmid_mi);

    _disable();
    if(hmapid.hm_pmiFirst == NULL)
        hmapid.hm_pmiFirst = pmi;
    else
        hmapid.hm_pmiLast->mi_pmi = pmi;
    hmapid.hm_pmiLast = pmi;
    _enable();
    return;
}
/***LP+ vdmmMarkVDM - Mark that the process is a VDM
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      None
 *
 */
VOID    PRIVENTRY   vdmmMarkVDM()
/*end*/
{
    _disable();
    pPTDACur->ptda_infoseg.LIS_ProcType = LIS_PT_REALMODE;
    ((struct InfoSegLDT *)(Lis2.is_va))->LIS_ProcType = LIS_PT_REALMODE;
    _enable();
    return;
}


/***LP+ vdmmVDMCreated - Mark that VDM creation is over
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      None
 *
 */
VOID    PRIVENTRY   vdmmVDMCreated()
/*end*/
{
PMAPID          pmi;

    pmi = hmapid.hm_pmiLast;
    vdmmAssertEQ(pmi->mi_hvdm,vdmid_HVDM);
    _disable();
    pmi->mi_fState = 0;
    _enable();
    return;
}

#pragma END_MVDM_GLOBAL_CODE
