#define SCCSID "@(#)vdmm.c      6.17 92/03/04"

/***    VDMM.C
 *
 *      IBM Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1992
 *
 *      All Rights Reserved
 *
 *  Exported Routines:
 *      VDMMInit
 *      VDMMKillVDM
 *      VDMMDispatchPageFault
 *      VDMMSwitchFgnd
 *      VDMMSwitchBgnd
 *      VDMMLoadVDD
 *      VDMMUnloadVDD
 *      VDMMstrncpyz
 *
 *  Imported Routines:
 *      LHSetupInst
 *      LHCheckForFault
 *      vdmmEnterTerminateVDM
 *      vdmmExitTerminateVDM
 *      vdmmGetMachineInfo
 *      vdmmInitGDT_PTDA
 *      LDRLoadVDD();
 *      LDRUnloadVDD();
 *
 *  History:
 *      08-Feb-1989 bws Slight changes to EP function headers for SDR.
 * 6.12 08/18/91 Jeff Muir  CP20 B725703  Added Global sysvalue for PIC Mask
 * 6.13 08/21/91 Stacey Barnes CP20 B723649 Uncomment vdmmFailMessage calls,
 *                sysinit was fixed so these calls will work now.
 * 6.14 08/27/91 Stacey Barnes CP20 B726295 VDMMDHRegisterPDD was not
 *                supporting de-registering a PDD entry point. Also,
 *                resident allocations for VDMMDHRegisterPDD were not
 *                being freed up at the end of init time.
 * 6.15 09/09/91 Stacey Barnes CP20 DCR1388 Add SystemConfigTable as Global
 *                System Value
 * 6.16 10/26/91 Steve Best CP20 B729110 Remove perfview counter
 * 6.17 03/02/92 Bill Madden CP20 B733891 Make vpvdmIRet a sys value
 * 1.2  09/15/92 r205 p053352 JD Muir  Do not reserve 256K at init time for Heap
 * 1.3  09/28/92 r206 f053758 WW Madden Added VPM begin/end user hooks
 * 1.4  10/19/92 r206 f056061 SA Barnes Added Boot Drive to GSYSVAL
 * 1.5  03/20/93 r206 f063248 JA Grimm  Added MMIOAddr for MMIO perf hooks.
 *      09/19/94                 bjn    Changed segment names for page tuning.
 */

#define FNO_NUM  FNO_VDMM_C
#include <fnomvdm.h>

#define INCL_NONE
#define INCL_VDH
#define INCL_VDHVKBD
#define INCL_VKBDINTERFACES

#include <vdmvdh.h>
#include <basemid.h>
#include <dem.h>
#include <kutil.h>
#include <string.h>
//#include <pvwxport.h>                                                //6.16
//#include <perfvw.h>                                                  //6.16

// External references

extern  struct      infoseg_s       Sis2;
extern  UCHAR       ABIOS_present;
extern  UCHAR       ABIOS_Revision;             // 6.15
extern  ULONG       flGlobalActions;            /* Lo byte is Resched flag */
extern  UCHAR       TCResched;
extern  struct      InfoSegGDT      SISData;
extern  struct      infoseg_s       Lis2;
extern  HEAPINIT    ahpi[];
extern  HEAP        ahp[];
extern  PFN         fpfnINTVDMIRQRouter;
#ifdef MMIOPH
extern  UCHAR       *RMT;                       /* Ptr to ras_mec_table */
#endif

extern  ULONG       PRIVENTRY       LHSetupInst(ULONG,ULONG);
extern  ULONG       PRIVENTRY       LHCheckForFault(PVOID);
extern  INT         PASCAL          vdmmEnterTerminateVDM(VOID);
extern  INT         PASCAL          vdmmExitTerminateVDM(VOID);
extern  ULONG       PASCAL          vdmmGetMachineInfo(VOID);
extern  VOID        PRIVENTRY       vdmmInitGDT_PTDA(VOID);
extern  VOID        PRIVENTRY       vdmmRemoveSymMTE(PMODULEHANDLE);
extern  BOOL        PRIVENTRY       fixAssignAddress(VOID);
extern  VOID        PRIVENTRY       vdmmFreeVDDEP(ULONG);
extern  VOID        PASCAL          vdmmSem(HSEM, ULONG);
extern  HVDM        PRIVENTRY       GetVDMHandle(USHORT,BOOL,BOOL);
extern  PHDLVDD     PRIVENTRY       OpenVDD(PSZ,BOOL);
extern  BOOL        PRIVENTRY       RequestVDD(HVDD,HVDM,ULONG,PVOID,PVOID);


INT   PASCAL VDMMInit(PSZZ,PSZZ,PVDMCONFIG,struct vdminitconfig_s *);
BOOL  EXPENTRY _loadds VDMMGETPOSREGS(VOID);     /* SysInit Get POS Registers */

// Local procedure prototypes

VOID        PRIVENTRY initTime            (BOOL                  );
BOOL        PRIVENTRY initAllocSem        (VOID                  );
VOID        PRIVENTRY initGlobalMap       (VOID                  );
VOID        PRIVENTRY initMapID           (VOID                  );
BOOL        PRIVENTRY initAssignAddress   (VOID                  );
BOOL        PRIVENTRY initInstance        (VOID                  );
ULONG       PRIVENTRY LoadVdds            (PSZZ,PVDMINITCONFIG,BOOL);
BOOL        PRIVENTRY initGetVDMInterruptHandler(VOID            );
VOID        PRIVENTRY vdmmFailMessage     (ULONG, PVDMINITCONFIG);
VOID        PRIVENTRY UnloadVDDs          (BOOL                  );
VOID        PRIVENTRY vdmmFreeRegisterPDDBlocks(VOID             ); // 6.14
VOID        PRIVENTRY initSysConfigTable  (VOID                  ); // 6.15
VOID        PRIVENTRY vdmmProcFBgnd       (USHORT,           BOOL);
VOID        PRIVENTRY vdmmProcPDB         (ULONG ,           BOOL);
VOID        PRIVENTRY vdmmProcMemMapped   (ULONG,INT,ULONG,  BOOL);
VOID        PRIVENTRY vdmmProcCreateDone  (VOID                  );
BOOL        PRIVENTRY pfnUndoHMAPID       (VOID                  );
VOID        PRIVENTRY pfnUndoFAULTS       (VOID                  );
VOID        PRIVENTRY pfnUndoLA           (VOID                  );
VOID        PRIVENTRY pfnUndoVDD          (VOID                  );
VOID        PRIVENTRY pfnUndoEM86         (VOID                  );
VOID        PRIVENTRY pfnUndoFixedHeap    (VOID                  );
VOID        PRIVENTRY pfnUndoSwapHeap     (VOID                  );
BOOL        PRIVENTRY vdmmVDMTerminating  (PID                   );
BOOL        PRIVENTRY initCheckVDDLoading (PVDMINITCONFIG        );
VOID        EXPENTRY  VDMMstrncpyz        (ULONG, PSZ, PSZ       );
ULONG       PRIVENTRY pfninfo             (SGID,ULONG,ULONG,PVOID,ULONG,PVOID);

/* POS initialization prototypes */
BYTE        PASCAL    vdmmGetPort90(void);
BYTE        PASCAL    vdmmGetPort92(void);
VOID        PASCAL    vdmmGetPlanarPOS(PPOS pPOS);
VOID        PASCAL    vdmmGetVideoPOS(PPOS pPOS);
VOID        PASCAL    vdmmGetSlotPOS(ULONG Slot,PPOS pPOS);

// VDMM's Init Data

#pragma BEGIN_MVDM_INIT_DATA

// VDD modules list head
PMODULEHANDLE   pmhGlobal=NULL;     // VDD Module handle head(Global)
PMODULEHANDLE   pmhLocal=NULL;      // VDD Module handle head(For LDRLoadVDD)
USHORT          hmteCur;            // Module handle for VDD being loaded

// PDD entry point list heads
PPDDEP          ppddepHead =NULL;   // PDD entry point list head

// VBIOS entry point for POST init calling to check success of
// essential installable VDDs.
PVDDPOSTINIT    pfnVbiosPostInit = NULL;

// Name of VPIC VDD
PSZ             pszVPIC = "VPIC";

#pragma END_MVDM_INIT_DATA



// VDMMs resident global data

#pragma BEGIN_MVDM_GLOBAL_DATA

// MVDM state flags;
// BUGBUG 22-Aug-90 JeffPar:  Two bits would serve as well as two dwords
BOOL           fIsMVDMPresent=FALSE;// MVDM Present flag
BOOL           fIsInitTime=FALSE;   // Boot Time flag

// Global linear heap. Created at MVDM initialization time
LA             laGlobalMap;         // Linear Arena For VDM Global Memory Map

// A structure which stores important IDs of a VDM
HMAPID         hmapid;              // Pid/SGid to HVDM Mapping List Head

// VDHGet/SetError array. For every interrupt level there is a flag.
// index 0 represent task time. 1 is interrupt level 0, etc.
ULONG         aErrorCode[MAX_INT_NEST];

// VPL Idle detection service addresses
NPFN           anpfnVPL[4] = {(NPFN)NULL,(NPFN)NULL,(NPFN)NULL,(NPFN)NULL};
PSZ            pszVpl = VPL_NAME;

// Initial state of PIC Mask ports                           6.12
// set during SYSINIT timeframe from real PIC mask
ULONG          ulPICMaskState=0xFFFF;

#pragma END_MVDM_GLOBAL_DATA



// VDMM's swappable global data

#pragma BEGIN_MVDM_SWAP_DATA

// VDM Events data table
EH aeh[] = {
    {FIFO_ORDER,  NULL,   NULL},    // VDM_CREATE
    {FIFO_ORDER,  NULL,   NULL},    // VDM_TERMINATE
    {FIFO_ORDER,  NULL,   NULL},    // VDM_FOREGROUND
    {FIFO_ORDER,  NULL,   NULL},    // VDM_BACKGROUND
    {FIFO_ORDER,  NULL,   NULL},    // VDD_EXIT
    {LIFO_ORDER,  NULL,   NULL},    // VDM_VDD_CREATE_DONE
    {FIFO_ORDER,  NULL,   NULL},    // VDM_PDB_DESTROY
    {FIFO_ORDER,  NULL,   NULL},    // VDM_PDB_CHANGE
    {FIFO_ORDER,  NULL,   NULL},    // VDM_CODEPAGE_CHANGE
    {FIFO_ORDER,  NULL,   NULL},    // VDM_TITLE_CHANGE
    {FIFO_ORDER,  NULL,   NULL},    // VDM_MEMORY_MAPPED_IN
    {FIFO_ORDER,  NULL,   NULL},    // VDM_MEMORY_UN_MAPPED
    {FIFO_ORDER,  NULL,   NULL},    // VDM_CREATE_DONE
    {FIFO_ORDER,  NULL,   NULL},    // VDM_BEGIN_VPM_TASK        // 1.3
    {FIFO_ORDER,  NULL,   NULL},    // VDM_END_VPM_TASK          // 1.3
};

// POS register values
POSREGS  PS2POSRegs;
PPOSREGS pPS2POSRegs=&PS2POSRegs;

// System Configuration Table
SYS_CONFIG_TABLE  System_config_table;                           // 6.15
PSYS_CONFIG_TABLE pSystem_config_table=&System_config_table;     // 6.15

// Count of VDM events
ULONG   nevents = (sizeof(aeh) / sizeof(EH));

// Number of VDMs in the system
ULONG   nVDMCreated;

// Global variables
ULONG   ulMachineType = MACHINE_TYPE_AT;
PVOID   pBlackHole;                 // Black hole start address
ULONG   cbBlackHole;                // size of blackhole
extern  PVOID pvdmArpl;
extern  VPVOID vpvdmIRet;           // 6.17

// Addresses and sizes for VDD instance data and MVDM kernel instance data
// heaps.
ULONG   cbKrnlInstance = 0;         // MVDM kernel instance data size(Fixed)
ULONG   cbKrnlInstSwap = 0;         // MVDM kernel instance data size(Swap)
PVOID   pKrnlInstance;              // Global add of kernel inst. data (Fixed)
PVOID   pKrnlInstSwap;              // Global add of kernel inst. data (Swap)
PVOID   pVDDFixedInstStart;         // VDD instance data (fixed) start address
PVOID   pVDDSwapInstStart;          // VDD instance data (swap)  start address
PVOID   pKrnlFixed;                 // VDD instance data (fixed) start address
PVOID   pKrnlSwap;                  // VDD instance data (swap)  start address

// Lock handles returned by VDHLockMem
LOCKENTRYHEAD lockentryhead;        // Head of lock entries
PLOCKENTRYHEAD plockentryhead=&lockentryhead; // pointer to lock entries

// VDD handles returned with DOSOpenVDD API
PHDLVDD phdlVddHead=NULL;           // VDD Handle list head

// VDD entry point list heads
PVDDEP  pvddepHead =NULL;           // Head of VDD entry points

// Block Pools list head
PBPH    pbphHead;                   // Block Pools Head

// Mutex Semaphore Handle for VDM Manager
HKSEM   hksemHVDD;                  // VDD handles mutex
HKSEM   hksemHFault;                // Fault handlers mutex
HKSEM   hksemHLock;                 // Lock entries mutex

// Global system values of use to VDHQuerySysValue
GSYSVAL  aGSV[] = {
    {&SISData.SIS_DayDate           ,MASKUCHAR },
    {&SISData.SIS_MonDate           ,MASKUCHAR },
    {&SISData.SIS_YrsDate           ,MASKUSHORT},
    {&SISData.SIS_DOWDate           ,MASKUCHAR },
    {&SISData.SIS_HrsTime           ,MASKUCHAR },
    {&SISData.SIS_MinTime           ,MASKUCHAR },
    {&SISData.SIS_SecTime           ,MASKUCHAR },
    {&SISData.SIS_HunTime           ,MASKUCHAR },
    {&SISData.SIS_BigTime           ,MASKULONG },
    {&SISData.SIS_TimeZone          ,MASKUSHORT},
    {&SISData.SIS_MsCount           ,MASKULONG },
    {&SISData.SIS_ClkIntrvl         ,MASKUSHORT},
    {&SISData.SIS_Dynamic           ,MASKUCHAR },
    {&SISData.SIS_MaxWait           ,MASKUCHAR },
    {&SISData.SIS_MinSlice          ,MASKUSHORT},
    {&SISData.SIS_MaxSlice          ,MASKUSHORT},
    {&flGlobalActions               ,MASKUCHAR },        //Yield (Resched)
    {&TCResched                     ,MASKUCHAR },        //TCYield
    {&SISData.SIS_VerMajor          ,MASKUCHAR },
    {&SISData.SIS_VerMinor          ,MASKUCHAR },
    {&SISData.SIS_RevLettr          ,MASKUCHAR },
    {&ulMachineType                 ,MASKULONG },
    {&pBlackHole                    ,MASKULONG },
    {&cbBlackHole                   ,MASKULONG },
    {&SISData.SIS_CurScrnGrp        ,MASKUCHAR },
    {&SISData.SIS_MaxScrnGrp        ,MASKUCHAR },
    {&SISData.SIS_MaxVioWinSG       ,MASKUCHAR },        //Window sessions
    {&SISData.SIS_MaxPresMgrSG      ,MASKUCHAR },        //PM Sessions
    {&SISData.SIS_MaxVDMs           ,MASKUCHAR },
    {&pvdmArpl                      ,MASKULONG },
    {&pSystem_config_table          ,MASKULONG },  // 6.15 System Config Table
    {&pPS2POSRegs                   ,MASKULONG },  //      POS Regs structure
    {&ulPICMaskState                ,MASKULONG },  // 6.12 Original PIC state
    {&vpvdmIRet                     ,MASKULONG },  // 6.17 addr of IRET in ROM
    {&SISData.SIS_BootDrv           ,MASKUSHORT},  // 1.4 Boot drive
    {&SISData.SIS_MMIOAddr          ,MASKULONG },  //      Mem mapped I/O addr
#ifdef MMIOPH
    {&RMT                           ,MASKULONG }   //      RAS Maj Event Codes
#else
    {0                              ,MASKULONG }   //      RAS Maj Event Codes
#endif
};

#pragma END_MVDM_SWAP_DATA



// VDMM's Instance data (prefix vdmid)

#pragma BEGIN_MVDM_INSTANCE_DATA

ULONG       vdmid_VDMSIG;           // VDM signature
MAPID       vdmid_mi;               // VDM's Local info
HVDM        vdmid_HVDM;             // Handle of VDM
ULONG       vdmid_ulFreezeCnt;      // Count of freeze calls
PTCB        vdmid_mphvdmptcb;       // maps hvdm to PTCB
PPTDA       vdmid_mphvdmpptda;      // maps hvdm to PPTDA
HPFH        vdmid_hpfh;             // Page Fault Handler  List Head
HLA         vdmid_hla;              // VDM local linear arena
ULONG       vdmid_flVDM=VDM_TITLECHANGE;

#pragma END_MVDM_INSTANCE_DATA

#pragma BEGIN_MVDM_SWAP_INSTANCE

BYTEHEAP    vdmid_DosHeap;          // Dos Arena Heap (byte granular)
PHEAP       vdmid_phpFixed;         // VDM's instance heap(fixed VDD data)
PHEAP       vdmid_phpSwap;          // VDM's instance heap(swappable VDD data)
UNDOPFN     vdmid_apfnUndo[CREATE_UNDO_ITEMS]; // VDM creation undo functions
BOOL        vdmid_fFgnd=VDM_INIT;   // Temporary hack to work around sesmgr
ULONG       vdmid_RMSIZE;           // RMSIZE as passed at create time
CHAR        vdmid_achTitle[16];     // VDM Title (Only 13 char are used)
ULONG       vdmid_vpicBases=0;      // Vpic Base Vectors

LSYSVAL       vdmid_aLSV[] = {      // Per VDM Sys Values of VDHQuerySysValues
    {&vdmid_HVDM        ,MASKULONG    ,LSV_INSTANCE  },
    {&vdmid_mi.mi_Pid   ,MASKUSHORT   ,LSV_INSTANCE  }, //PID
    {NULL               ,MASKULONG    ,LSV_GLOBAL    }, //PCRF set at creation
    {&vdmid_mi.mi_SGid  ,MASKUSHORT   ,LSV_INSTANCE  }, //SGID
    {&vdmid_fFgnd       ,MASKULONG    ,LSV_INSTANCE  },
    {&vdmid_RMSIZE      ,MASKULONG    ,LSV_INSTANCE  },
    {NULL               ,MASKUSHORT   ,LSV_GLOBAL    }, //Codepage ID
    {NULL               ,MASKUCHAR    ,LSV_GLOBAL    }, //Priority class
    {NULL               ,MASKUCHAR    ,LSV_GLOBAL    }, //Priority level
    {&vdmid_vpicBases   ,MASKULONG    ,LSV_INSTANCE  }  //Vpic base vector
};

#pragma END_MVDM_SWAP_INSTANCE



// VDMM's Init code

#pragma BEGIN_MVDM_INIT_CODE

/***EP+ VDMMInit - VDM manager initialization
 *
 *      SysInit calls here to initialize MVDM.
 *
 *      This routine initializes the VDM Manager, and overall MVDM support
 *      by loading all the VDDs passed in.  Each VDD's Init routine is called
 *      as each VDD is loaded (by the kernel's loader).
 *
 *      If a Base VDD load fails, MVDM support is disabled.
 *      "Installable" VDD loading failure has no impact on MVDM
 *      support.  The code/data of the failed installable VDD is
 *      released.
 *
 *      ENTRY
 *          pszzBaseVDDs     - pointer to base VDD names
 *          pszzOtherVDDs    - pointer to installable VDD names
 *          pvdmconfig       - pointer to VDMCONFIG structure
 *          pvdminitconfig   - pointer to VDMINITCONFIG structure
 *
 *      EXIT
 *          SUCCESS
 *              NO_ERROR
 *          FAILURE
 *              !0:
 *
 *      CONTEXT
 *          System Initialization
 *              All PDD loading complete
 *              Paging enabled
 *              No more real-mode code executed
 *
 *      PSEUDO-CODE
 *          Mark the time to be boot time
 *          Initialize global memory map
 *          Initialize event handler table
 *          Initialize MAPID
 *          Allocate sparse object of 1Mb in system arena for VDD code
 *          Allocae sparse object of 64k in system arena for VDD global data
 *          Create and initialize three heaps i.e.
 *                  VDD CODE,GLOBAL and INSTANCE data
 *          For each entry in pszBaseVDDs
 *                  Load the VDD
 *                  If error in loading
 *                      Display a mesage that MVDM not operational
 *                      Free VDD modules
 *                      return failure
 *          For each entry in pszOtherVDDs
 *                  Load the VDD
 *                  If error in loading
 *                      Free this VDD module
 *                      Deassign address reserved in VDD heaps by Loader
 *                      Display a message for this VDD failure
 *                  else
 *                      Clear fields in VDD heaps to show this success
 *          endfor
 *          Create an entry in Global heap for MVDM Kernel Instance data
 *          Copy instance data heap entries in global map as a Linear record
 *          Store pvdmconfig in VDM global info to be used with DosCreateVDM
|*          Get hardware interrupt handler entrypoint from VPIC
 *          mark MVDM present
 *          return success
 */

INT EXPENTRY VDMMInit(pszzBaseVDDs,pszzOtherVDDs,pvdmconfig,pvdminitconfig)
PSZZ          pszzBaseVDDs;
PSZZ          pszzOtherVDDs;
PVDMCONFIG    pvdmconfig;
PVDMINITCONFIG  pvdminitconfig;
/*end*/
{

    register PVDMINITCONFIG p = pvdminitconfig;
    INT rc;

    // Convert 16:16 pointers to 0:32 pointers
    p->pFailureBuf = (PSZ)F16PTOLIN((ULONG)p->pFailureBuf);
    p->pusErrorCode = (PUSHORT)F16PTOLIN((ULONG)p->pusErrorCode);

    // Initialize kernel stack for VDHQuerySel
    vdmmInitGDT_PTDA();

    // Mark MVDM init time
    initTime(TRUE);

    // Grab all the semaphores needed by VDMM component
    if(rc = initAllocSem ())
        return rc;

    // Initialize global linear arena
    initGlobalMap();

    // Initialize Id to hvdm table
    initMapID();

    // Initialize lock entry pointers for VDHLockMem
    plockentryhead->phle_leFirst=NULL;
    plockentryhead->phle_leLast=NULL;

    // Init values for VDHQuerySysValue
    if (ABIOS_present)
        ulMachineType = MACHINE_TYPE_PS2;

    // Initialize System Configuration Table
    initSysConfigTable();                      // 6.15

    // Initialize VDD heaps
    if(!initAssignAddress())
        return(ERROR_VDM_DOWN);

    // Set default properties to config.sys values
    if ((rc=vdmSetPropertyDefaults(pvdmconfig)) != NO_ERROR)
        return rc;

    // Call 8086 emulation initialization
    if((rc = EM86Init()) != NO_ERROR)
        return(rc);

    // Call DOSEmulation initialization
    if((rc=DEMInit()) != NO_ERROR)
        return(rc);

    // Load Base VDDs
    if(pszzBaseVDDs != NULL)
        if((rc = LoadVdds(pszzBaseVDDs, pvdminitconfig, TRUE)) != NO_ERROR)
            return(rc);

    // Load installable VDDs
    if(pszzOtherVDDs != NULL)
        LoadVdds(pszzOtherVDDs, pvdminitconfig, FALSE);

    // Check that all essential installable VDDs got loaded successfully
    if (initCheckVDDLoading(pvdminitconfig))
        return(ERROR_VDM_DOWN);

    // Process different heaps to get maximum size used and
    // dereserve the unused space
    if(fixAssignAddress() == FALSE)
        return(ERROR_VDM_DOWN);

    // Create entries in global heap for kernel and VDD instance data.
    if(initInstance() == FALSE)
        return(ERROR_VDM_DOWN);

    // Get Hardware interrupt router for VDMS from VPIC
    if(initGetVDMInterruptHandler() == FALSE)
        return(ERROR_VDM_DOWN);

    // No VDM in the system
    nVDMCreated = 0;

    // Register VDD for info usage
    VDHRegisterVDD("infovdd$",&pfninfo,NULL);

    // Free up the memory used for VDMMDHRegisterPDD
    vdmmFreeRegisterPDDBlocks();                      // 6.14

    // MVDM Init time is over
    initTime(FALSE);

    // return 0 for SUCCESS
    return(NO_ERROR);
}

/***LP+ initTime - Mark boot time and protmode only as per fl.
 *
 *      ENTRY
 *          None
 *
 *      EXIT
 *          None
 */

VOID PRIVENTRY initTime(fl)
BOOL    fl;
/*end*/
{

    SISData.SIS_ProtMdOnly = (char) fl;
    ((struct InfoSegGDT *)(Sis2.is_va))->SIS_ProtMdOnly = (char) fl;
    fIsInitTime = fl;
    fIsMVDMPresent = !fl;
    return;
}

/***LP+ initAllocSem - Allocate semaphores required by VDMM component.
 *
 *      ENTRY
 *          None
 *
 *      EXIT
 *          SUCCESS
 *              returns 0
 *          FAILURE
 *              returns ERROR_NOT_ENOUGH_MEMORY
 *
 *      CONTEXT
 *          INnit
 */

BOOL PRIVENTRY initAllocSem()
/*end*/
{
INT  rc;

    // Allocate a sempahore for VDD handle related MUTEXs
    if((rc = KSEMCreate(&hksemHVDD,KRNL_MUTEXSEM)) != NO_ERROR)
        return rc;

    if((rc = KSEMCreate(&hksemHFault,KRNL_MUTEXSEM)) != NO_ERROR){
        KSEMDestroy (hksemHVDD);
        return rc;
    }

    if((rc = KSEMCreate(&hksemHLock,KRNL_MUTEXSEM)) != NO_ERROR){
        KSEMDestroy (hksemHVDD);
        KSEMDestroy (hksemHFault);
        return rc;
    }

    return NO_ERROR;
}

/***LP+ initGlobalMap - Initialize global memory map
 *
 *      ENTRY
 *          None
 *
 *      EXIT
 *          None
 */

VOID PRIVENTRY initGlobalMap()
/*end*/
{

    // Initialize all the instance data start addresses

    // Fixed Kernel instance data strats at MVDM_INST_DATA_ADDR. Its maximum
    // size is 64k.
    pKrnlFixed = (PVOID)MVDM_INST_DATA_ADDR;
    vdmmAssertLTE(cbKrnlInstance,FIXED_KRNL_INST_SIZE);

    // Swap Kernel instance data strats at MVDM_SINST_DATA_ADDR
    pKrnlSwap = (PVOID)MVDM_SINST_DATA_ADDR;

    // Make size dword aligned
    cbKrnlInstance = ROUND_UP(cbKrnlInstance,4);
    cbKrnlInstSwap = ROUND_UP(cbKrnlInstSwap,4);

    // Get VDD fixup addresses; i.e after respective kernel instance data.
    pVDDFixedInstStart=(PVOID)((ULONG)pKrnlFixed + cbKrnlInstance);
    pVDDSwapInstStart=(PVOID)((ULONG)pKrnlSwap + cbKrnlInstSwap);

    // VDM Global Memory Map Initialization
    laGlobalMap.la_plrFirst = NULL;
    laGlobalMap.la_plrLast = NULL;

    // 1.2 r205
    // To allow for EMS having more than 384K of low memory pages
    // we should not reserve any memory until VDM creation time
/*  // Reserve first 256k for VDHAllocDosMem
    if(VDHReservePages((PVOID)0,(ULONG)64) == 0)
        Panic(("initGlobalMap:Failed"));  */

    return;
}

/***LP+ initMapID - Initialize PID/SGID -> HVDM mapping table
 *
 *      ENTRY
 *          None
 *
 *      EXIT
 *          None
 */

VOID PRIVENTRY initMapID()
/*end*/
{
    // Initialize Id to Handle Mapping structure
    hmapid.hm_pmiFirst=hmapid.hm_pmiLast=NULL;
    return;
}

/***LP+ initInstance - Make records in global map for fixed and swappable
 *                     instance data.
 *
 *      ENTRY
 *          None
 *
 *      EXIT
 *          SUCCESS - TRUE
 *          FAILURE - FALSE
 */

BOOL PRIVENTRY initInstance()
/*end*/
{
ULONG  cbFixedInst,cbSwapInst;

    // Get page granular size of fixed VDD instance heap
    cbFixedInst= cbKrnlInstance + ahp[VDMAA_OT_FIXEDINSTANCE].hp_cbMax;
    cbFixedInst=ROUND_UP(cbFixedInst,PAGESIZE);

    // Get page granular size of Swappable VDD instance heap
    cbSwapInst= cbKrnlInstSwap + ahp[VDMAA_OT_SWAPINSTANCE].hp_cbMax;
    cbSwapInst=ROUND_UP(cbSwapInst,PAGESIZE);

    if(LHSetupInst(cbFixedInst,cbSwapInst) == FALSE)
        return FALSE;
    else
        return TRUE;
}

/***LP  Check that all essential installable VDDs were loaded
 *      successfully.
 *
 *      ENTRY - pvdminitconfig - Buffer for MVDM error reporting thread
 *
 *      EXIT
 *          SUCCESS - return 0
 *          FAILURE - returns 1
 */
BOOL PRIVENTRY initCheckVDDLoading (pi)
PVDMINITCONFIG  pi;
{
PSZ     pszVDD =NULL;

    if(pszVDD = (PSZ)((*pfnVbiosPostInit)())){
        VDMMstrncpyz(pi->ulcbFailureBuf, pszVDD, pi->pFailureBuf);
        vdmmFailMessage(ERROR_DEV_NOT_EXIST, pi);        // 6.13
        UnloadVDDs(TRUE);
        return 1;
    }
    return 0;
}

/***LP+  LoadVdds - Load VDDs
 *
 *      ENTRY
 *          pszzVDDs - VDD list
 *          pvdminitconfig -- pointer to the VDMINITCONFIG structure
 *                            passed to VDMMInit
 *          fBase          -- should we stop immediately on failure?
 *
 *      EXIT
 *          SUCCESS - returns NO_ERROR
 *          FAILURE - returns an error code
 *                      ERROR_FILE_NOT_FOUND
 *                      ERROR_GEN_FAILURE           // install failed
 */

ULONG PRIVENTRY LoadVdds(pszzVDDs, pi, fBase)
PSZZ    pszzVDDs;
PVDMINITCONFIG pi;
BOOL    fBase;
/*end*/
{
PCHAR       pch;
INT         rc;
BOOL        rcInit;
ldrmi_t     ldrmi;

    while(*pszzVDDs != '\0') {

        // Change the space used as a name/parameters separator to a NULL
        pch = pszzVDDs;
        while(*pch != ' ') {
            if(*pch++ == '\0') {
                pch = NULL;
                break;
            }
        }
        if(pch != NULL) {
            *pch = '\0';
        }

        // Interface to load VDD
        if((rc = LDRLoadVDD(pszzVDDs)) != NO_ERROR) {
            vdmmAssertEQ(pmhLocal,NULL);

            // Change the NULL between name/parameters back to a space
            if(pch != NULL) {
                *pch++ = ' ';   // pch is the beginning of the parameters
            }

            VDMMstrncpyz(pi->ulcbFailureBuf, pszzVDDs, pi->pFailureBuf);
            UnloadVDDs(fBase);
            if(fBase){
                return(rc);
            }
            // An installable VDD failed; putup message and continue.
            vdmmFailMessage(rc, pi);                      // 6.13
        }
        else {
            vdmmAssertNEQ(pmhLocal,NULL);

            // Change the NULL between name/parameters back to a space
            if(pch != NULL) {
                *pch++ = ' ';       // pch is the beginning of the parameters
            }

            // If no init routine exists, LDRGLOBALSETUP will be set.
            // The funny && expression is so that the nonexistence
            // of an init routine returns rc = ERROR_BAD_EXE_FORMAT.
            // If an init routine does not exist this produces an
            // invalid VDD message, not a did not install message.

            if((rc = LDRGetTKModuleInfo(pmhLocal->mh_hmte,SSToDS(&ldrmi)))
              != NO_ERROR ||
              ((ldrmi.ldrmi_attrib & LDRGLOBALSETUP) &&
              (rc = ERROR_BAD_EXE_FORMAT))) {

                VDMMstrncpyz(pi->ulcbFailureBuf, pszzVDDs, pi->pFailureBuf);
                UnloadVDDs(fBase);
                if(fBase) {
                    return(rc);
                }
                vdmmFailMessage(rc, pi);                 // 6.13
            }
            else {

                // Call init routine. hmteCur is a global variable which
                // will be initialized here for use by VDHInstallUserHook.
                hmteCur = pmhLocal->mh_hmte;
                rcInit = (*(PVDDINIT)ldrmi.ldrmi_startaddr.ptr_flat)(pch);
                if(rcInit == FALSE) {

                    VDMMstrncpyz(pi->ulcbFailureBuf, pszzVDDs, pi->pFailureBuf);
                    UnloadVDDs(fBase);
                    if(fBase) {
                        return(ERROR_INIT_ROUTINE_FAILED);
                    }
                    vdmmFailMessage(ERROR_INIT_ROUTINE_FAILED, pi);    // 6.13
                }
                else {
                    // Make call to remove MTE/symbol linkage so we can
                    // free the MTE without freeing the symbols
                    vdmmRemoveSymMTE(pmhLocal);
                    if (pfnVbiosPostInit == NULL)
                        pfnVbiosPostInit = (PVDDPOSTINIT)rcInit;
                }
            }

            // Copy pmhLocal in Global list and mark pmhLocal as NULL
            // for next VDD loading
            if(pmhLocal){
                pmhLocal->mh_pmh = pmhGlobal;
                pmhGlobal = pmhLocal;
                pmhLocal = NULL;
            }
        }
        pch = NULL;
        pszzVDDs += strlen(pszzVDDs) + 1;
    }
    return(NO_ERROR);
}

/***LP  vdmmFailMessage -- display a VDD-loading message
 *
 *      The failure cause buffer is assumed to have already been filled
 *      with an appropriate failure cause.
 *
 *      ENTRY
 *          ulErrorCode    -- error code to return
 *          pvdminitconfig -- pointer to the VDMINITCONFIG structure
 *                            passed to VDMMInit
 *
 *      EXIT
 *          None
 *
 *      METHOD
 *          set pusErrorCode to the error code
 *          set the "Error handler busy" semaphore
 *          clear the "Message buffer empty" semaphore
 *          Wait on the "Error handler busy" semaphore
 */

VOID PRIVENTRY vdmmFailMessage(ulErrorCode, pvdminitconfig)
ULONG ulErrorCode;
register PVDMINITCONFIG pvdminitconfig;
{
    *pvdminitconfig->pusErrorCode = (USHORT)ulErrorCode;
    vdmmSem(pvdminitconfig->hsemErrHBusy,0);
    vdmmSem(pvdminitconfig->hsemBufEmpty,1);
    vdmmSem(pvdminitconfig->hsemErrHBusy,2);
}

/***LP+ UnloadVDDs - Unload VDDs
 *
 *  Unload  specified VDDs
 *
 *  ENTRY
 *      fBase - Was it a Base VDD that failed
 *
 *  EXIT
 *      None
 *
 *  NOTES
 *      When we call LDRLoadVDD it calls back VDMM with VDMMLoadingVDD
 *      to notify a prticular VDD loading. If a VDD requires other VDDs
 *      to be loaded this notification will occur in the same order as
 *      the dependencies are among these VDDs. If LDRLaodVDD call fails
 *      it will call VDMMUnloadingVDD for each VDD it loaded with this
 *      call. If the VDD fails its init routine VDMM will call LDRUnLoadVDD
 *      with that hmte which was loaded first. LDRUnloadVDD will call
 *      VDMMUnloadingVDD for each loaded VDD as a result of loading hmte.
 *
 *  PCODE
 *      if a base VDD failed
 *          unload pmhLocal and pmhGlobal
 *      else
 *          unload pmhLocal
 */

VOID    PRIVENTRY   UnloadVDDs(fBase)
BOOL    fBase;
{

PMODULEHANDLE pmh;

    if(pmhLocal){
        if(LDRUnloadVDD(pmhLocal->mh_hmte) != NO_ERROR)
            Panic(("UnloadVDDs:Unloading Failed"));
    }
    vdmmAssertEQ(pmhLocal,NULL);

    if(fBase){
        pmh = pmhGlobal;
        while(pmhGlobal){
            if(LDRUnloadVDD(pmhGlobal->mh_hmte) != NO_ERROR)
                Panic(("UnloadVDDs:Unloading Failed"));
            pmh = pmhGlobal;                // was called.
        }
    }

    return;
}

/***EP+ VDMMLoadingVDD - Notification of a VDD loading
 *
 *      The loader calls here after starting to load a particular VDD,
 *      but prior to calling VDMMAssignAddress for any of that VDDs
 *      objects.
 *
 *      ENTRY
 *           hmte - MTE handle of the VDD being loaded
 *
 *      EXIT-SUCCESS
 *          returns 0;
 *
 *      EXIT-FAILURE
 *          returns !0; Loader should fail load.
 *
 *      CONTEXT
 *          Init
 *
 *      PCODE
 *          Record hmte in "being loaded" list;
 */

BOOL EXPENTRY VDMMLoadingVDD(hmte)
USHORT  hmte;
/*end*/
{
    PMODULEHANDLE pmh;

    // Allocate new handle
    if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(MODULEHANDLE),
                  (VMHOB)VDDMODOWNER, NA, SSToDS(&pmh)) == NO_ERROR) {
        pmh->mh_hmte = hmte;
        pmh->mh_pmh = NULL;
        pmhLocal = pmh;
        return 0;               // success
    }
    return 1;                   // failure
}

/***EP+ VDMMUnloadingVDD - Notification of a VDD unloading
 *
 *      The loader calls here when unloading a VDD.  This function must
 *      be called by the loader if it has called VDMMLoadingVDD for the
 *      VDD.  It must be called prior to any VDMMDeassignAddress calls
 *      for this VDD.
 *
 *      ENTRY
 *          hmte - MTE handle of the VDD being unloaded
 *
 *      EXIT-SUCCESS
 *          returns 0;
 *
 *      EXIT-FAILURE
 *          Internal Error - hmte is not a known VDD.
 *
 *      CONTEXT
 *          Init
 *
 *      PCODE
 *          Deregister all the entry points associated with this hmodule
 *          call VDD exit entry point if registered for this hmodule
 */

BOOL EXPENTRY VDMMUnloadingVDD(hmte)
USHORT  hmte;
/*end*/
{
ULONG   i;
PEL     pel,pelLast;
PMODULEHANDLE pmh;

    // Delink the pmh entry from local or global list
    if(pmhLocal == NULL || (pmhLocal != NULL && pmhLocal->mh_hmte != hmte)){
        pmh = pmhGlobal;
        if(pmh->mh_hmte != hmte)
            Panic(("VDMMUnloadingVDD:Invalid VDD hmte"));
        else{
            pmhGlobal = pmh->mh_pmh;
            VMFreeKHB(VM_HKH_PUB_RESRW,pmh);
        }
    }
    else{
        VMFreeKHB(VM_HKH_PUB_RESRW,pmhLocal);
        pmhLocal = NULL;
    }

    // Scan all VDM States for a matching hmte and free event entry
    for(i=0; i < nevents; i++){
        pel = aeh[i].eh_pelFirst;
        pelLast = (PEL)((PBYTE)(&aeh[i].eh_pelFirst) - FIELDOFFSET(EL, el_pel));
        while(pel != NULL && pel->el_hmte != hmte){
            pelLast = pel;
            pel = pel->el_pel;
        }

        if(pel != NULL){           // any match found ?
            if(i == VDD_EXIT)      // if yes and in EXIT type call exit handler
                ((PEXITH)*pel->el_pfn)();

            // delink the structure from list head
            pelLast->el_pel = pel->el_pel;

            // delink the structure from list tail
            if(aeh[i].eh_pelLast == pel) {
                pelLast->el_pel = NULL;
                aeh[i].eh_pelLast = pelLast;
            }
            VMFreeKHB(VM_HKH_PUB_RESRW,pel);
        }
    }

    // Dergister VDD entry points registered by VDHRegisterVDD
    vdmmFreeVDDEP (hmte);

    return 0;
}

/***LP+ initGetVDMInterruptHandler
 *
 *      This routine is called to get the entry point into VPIC, for
 *      hardware interrupt handling.
 *      ENTRY
 *              None
 *
 *      EXIT
 *          SUCCESS - TRUE
 *          FAILURE - FALSE
 *
 *      CONTEXT
 *          System initialization
 *
 *      PSEUDO-CODE
 *          Open VPIC
 *          Request interrupt handler entry point from VPIC
 */

BOOL PRIVENTRY initGetVDMInterruptHandler()
{
    HVDD    hvdd;

    if((hvdd = (HVDD)OpenVDD(pszVPIC, VDDVDD)) == FALSE)
        return(FALSE);

    if(RequestVDD(hvdd, 0, CMD_GET_INTERRUPT_HANDLER, NULL,
          &fpfnINTVDMIRQRouter) == FALSE)
        return(FALSE);

    return(TRUE);
}
/***LP+  VDMMDHRegisterPDD(pszName, fpfnPDDProc)
 *
 *      Register or De-register a PDD entry point for the given PDD name.
 *      This routine is called by dh_RegisterPDD after
 *      converting 16:16 PDDProc address to 16:32.
 *
 *      ENTRY
 *       pszName - PDD name (limited to MAXLENXDD chars) (16:16)
 *       fpfnPDDProc - PDD 16:32 entry point for VDMM, if NULL
 *                     then remove PDD entry point for the given
 *                     PDD name
 *        ENTRY
 *          uFunc - function code
 *                  PDDCMD_REGISTER (essential)
 *                  Other function codes depends on specific PDD-VDD pair
 *
 *          ul1   - function-specific;
 *                  if a pointer, it is 16:16,
 *                  and will generally be an input packet
 *
 *          ul2   - function-specific;
 *                  if a pointer, it is 16:16,
 *                  and will generally be an output packet
 *
 *          if uFunc == PDDCMD_REGISTER:
 *              ul1.off == code selector
 *              ul1.sel == zero
 *              ul2.off == low 16-bits of EIP for registered VDD subroutine
 *              ul2.sel == high 16-bits of EIP for registered VDD subroutine
 *
 *          if uFunc == PSDCMD_QUERYID: (Example)
 *              ul1     == NULL
 *              ul2.off == offset of output packet
 *              ul2.sel == selector of output packet
 *
 *        EXIT (SUGGESTED)
 *          SUCCESS - returns !0
 *          FAILURE - returns 0 (error code in BX)
 *
 *      EXIT
 *          SUCCESS - returns 0
 *          FAILURE - System Halt
 *      CONTEXT
 *          Initialization
 *
 *      NOTES
 *          There is nothing to stop a PDD from registering
 *          multiple VDD entry points under different PDD
 *          names.  The name is not used for any purpose other
 *          than to match it against a name supplied by the VDD
 *          on a VDHOpenPDD.  However, the normal case should
 *          be ONE (1) VDDProc per PDD.
 *
 *          A PDD that can be de-installed should call this service
 *          for each entry point it has registered.
 *
 *      PCODE
 *          return FAILURE if parameters are invalid
 *          Allocate PDD event handler structure
 *          Copy the PDD name, truncating to n characters if necessary
 *          update PDD entry point list
 *          return SUCCESS
 */

BOOL VDMMDHRegisterPDD(pszName, fpfnPDDProc)
PSZ            pszName;
FPFNPDD        fpfnPDDProc;
/*end*/
{
PPDDEP         ppddep,prev_ppddep;                                     // 6.14
PBYTE          p;

    // Get 0:32 address of PDD Name
    if((pszName = F16PTOLIN (pszName)) == (PVOID)-1)
        Panic(("VDMMDHRegisterPDD"));

    // Validate parameters
    if(strlen(pszName) > MAXLENXDD)
        Panic(("VDMMDHRegisterPDD:Invalid PDD Name"));

    ppddep=ppddepHead;

    // If the function is null deregister PDD else register it         // 6.14
    if (!(OFFSETOF32(fpfnPDDProc)) && !(SEGMENTOF32(fpfnPDDProc))) {   // 6.14

       prev_ppddep=ppddepHead;                                         // 6.14

       // Find matching PDD name                                       // 6.14
       while((ppddep != (PPDDEP)NULL) &&                               // 6.14
             (strcmp(ppddep->pddep_szPDD,pszName) != 0)){              // 6.14
           prev_ppddep = ppddep;                                       // 6.14
           ppddep=ppddep->pddep_ppddep;                                // 6.14
       }                                                               // 6.14

       // If PDD name was not found in the list then panic             // 6.14
       if (ppddep == (PPDDEP)NULL) {                                   // 6.14
           Panic(("VDMMDHRegisterPDD:PDD name not found"));            // 6.14
       } /* endif */                                                   // 6.14

       // Was the match the first in the list?                         // 6.14
       if (ppddep == prev_ppddep) {                                    // 6.14
           ppddepHead = ppddep->pddep_ppddep;                          // 6.14
       } else {                                                        // 6.14
           prev_ppddep->pddep_ppddep = ppddep->pddep_ppddep;           // 6.14
       } /* endif */                                                   // 6.14

       VMFreeKHB(VM_HKH_PUB_RESRW,ppddep);                             // 6.14

    } else {                                                           // 6.14

       // Check for duplicate PDD
       while(ppddep != (PPDDEP)NULL){
           if(strcmp(ppddep->pddep_szPDD,pszName) == 0)
               Panic(("VDMMDHRegisterPDD:Duplicate PDD"));
           ppddep=ppddep->pddep_ppddep;
       }
       if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(PDDEP), (VMHOB)VDDPDDEPOWNER, NA,
                     SSToDS(&p)) != NO_ERROR)
           Panic(("VDMMDHRegisterPDD:Heap Overflow"));

       // Register PDD Proc
       ppddep=(PPDDEP)p;
       strcpy(ppddep->pddep_szPDD,pszName);
       ppddep->pddep_fpfn=fpfnPDDProc;

       // Clear ring bits in selector (installable PDDs are init'd at ring 3!)
       SEGMENTOF32(ppddep->pddep_fpfn) &= (~3);

       ppddep->pddep_ppddep=ppddepHead;
       ppddepHead=ppddep;

    } /* endif */                                                      // 6.14

    return 0;
}

/***LP  VDMMGetPOSRegs - Initialize data for POS registers from real POS
 *
 *      This function is called by SysInit.
 *      POS register need to be emulated for the PS/2 Machines.
 *      A copy of these registers is made during SYSINIT.
 *
 *      ENTRY   NONE
 *
 *      EXIT    SUCCESS
 *                  return TRUE
 *              FAILURE
 *                  return FALSE
 *
 *      CONTEXT Initialization
 */
 BOOL EXPENTRY _loadds VDMMGETPOSREGS(void)
 {
   register ULONG  i;

// PS2POSRegs.VideoPOS.aPOSReg[2] = 0x01;       // mark video as enabled
   PS2POSRegs.Port94 = 0xFF;                    // planar, video enabled
   PS2POSRegs.Port96 = 0;                       // no adapter selected
   PS2POSRegs.Port90 = vdmmGetPort90();        // Central Arbiter
   PS2POSRegs.Port92 = vdmmGetPort92();        // System control Port

   for(i=0;i<POS_MAX_SLOT;i++) {                // Get POS information for each
     vdmmGetSlotPOS(i,&PS2POSRegs.SlotPOS[i]);  // slot in machine
     }

  vdmmGetPlanarPOS(&PS2POSRegs.PlanarPOS); // get planar pos info
  vdmmGetVideoPOS(&PS2POSRegs.VideoPOS);   // get video pos info

  return TRUE;
  }

/***LP+ vdmmFreeRegisterPDDInfo
 *
 *      This routine is called to walk the ppddepHead chain to
 *      free memory that was allocated to build the chain of
 *      PDDs that registered entry points for VDDs. This chain
 *      is needed at VDD init time only.
 *
 *      ENTRY
 *              None
 *
 *      EXIT
 *              None
 *
 *      CONTEXT
 *          System initialization
 *
 */

VOID PRIVENTRY vdmmFreeRegisterPDDBlocks()                              // 6.14
{                                                                       // 6.14

PPDDEP         ppddep,temp_ppddep;                                      // 6.14

    ppddep=ppddepHead;                                                  // 6.14

    while(ppddep != (PPDDEP)NULL){                                      // 6.14
       temp_ppddep = ppddep->pddep_ppddep;                              // 6.14
       VMFreeKHB(VM_HKH_PUB_RESRW,ppddep);                              // 6.14
       ppddep=temp_ppddep;                                              // 6.14
    }                                                                   // 6.14

    return;                                                             // 6.14
}                                                                       // 6.14

/***LP+ initSysConfigTable
 *
 *      This routine is called to set up the System Configuration Table
 *      that is accessed via VDHQuerySysValue.
 *
 *      ENTRY
 *              None
 *
 *      EXIT
 *              None
 *
 *      CONTEXT
 *          System initialization
 *
 */

VOID PRIVENTRY initSysConfigTable()         // 6.15
{
    ULONG   ulmodelsubmodel;

    ulmodelsubmodel = vdmmGetMachineInfo();
    pSystem_config_table->Model = BYTEOF(ulmodelsubmodel,1);
    pSystem_config_table->Submodel = BYTEOF(ulmodelsubmodel,0);
    pSystem_config_table->BIOS_revision = ABIOS_Revision;

    return;
}

#pragma END_MVDM_INIT_CODE

#pragma BEGIN_MVDM_GLOBAL_CODE

/***EP+ VDMMKillVDM - Terminate a VDM
 *
 *      Terminate the VDM and release all of its resources.
 *      This is called by Tasking when a VDM process is dying,
 *      before any other generic process cleanup is done.
 *
 *      This routine will also handle those VDM terminations which
 *      were marked a VDM but failed to complete creation. There
 *      is no difference from this routine's point of view whether
 *      a fully created VDM is being killed or a partial  one.
 *      In every VDM instance data there is a undo array which this
 *      routine has to enumerate to undo those work items which
 *      were done in this VDM creation after marking it a VDM.
 *
 *      ENTRY
 *          pid - Pid of terminating VDM
 *
 *      EXIT-SUCCESS
 *          None.
 *
 *      EXIT-FAILURE
 *          Internal Error.
 *
 *      CONTEXT
 *          Current VDM Task
 *
 *      PCODE
 *          Enumerate undo list which will do the following for a fully
 *          created VDM:
 *              Call VDDs to notify termination
 *              Call DEM to notify termination
 *              Call EM86 to notify termination
 *              Release VDMConfig Structure
 *              Release HMAPID
 *              Release Linear Heap
 *          Release Page faults
 */

BOOL EXPENTRY VDMMKillVDM(pid)
PID           pid;
/*end*/
{
ULONG   i;

    // Grab the Semaphore to Create/Terminate VDM
    if(vdmmEnterTerminateVDM() != NO_ERROR)
        return 0;

    //If MVDM is not operational or no VDM to kill return error
    if(ISMVDMPRESENT == 0 || nVDMCreated == 0){
        vdmmExitTerminateVDM();
        return 0;
    }

    // Check there exists a vdm of this pid and mark it terminating
    if(vdmmVDMTerminating(pid) == FALSE)
        Panic(("VDMMKillVDM:Invalid PID"));

    // Enumerate over undo list to undo the work
    for(i=0; i < CREATE_UNDO_ITEMS; i++){
        if(vdmid_apfnUndo[i])
            (*vdmid_apfnUndo[i])();
    }

    // Free DOS properties
    vdmDestroyPropertyInstance();

    // Clear vdm signature
    vdmid_VDMSIG = 0;

    // Undo Registered Page faults
    pfnUndoFAULTS();

    // Change to normal process
    // The following lines are commented because FS requires the process to
    // be a VDM when it closes out its files.
    // In the case of VDM_VMBOOT the process is really not a true VDM.
    if (vdmid_flVDM & VDM_VMBOOT)
    {
       _disable();
       pPTDACur->ptda_infoseg.LIS_ProcType = LIS_PT_FULLSCRN;
       ((struct InfoSegLDT *)(Lis2.is_va))->LIS_ProcType = LIS_PT_FULLSCRN;
       _enable();
    }

    // Decrement VDM count
    nVDMCreated--;

    // Release the Semaphore to Create/Terminate VDM
    vdmmExitTerminateVDM();
    return 0;
}

/***LP+ vdmmVDMTerminating -  Mark that VDM is terminating
 *
 *  ENTRY
 *      pid - VDM's process id
 *
 *  EXIT
 *      TRUE  - if pid valid
 *      FALSE - if pid invalid
 */
BOOL    PRIVENTRY   vdmmVDMTerminating (pid)
PID     pid;
{
PMAPID        pmi;

    pmi=hmapid.hm_pmiFirst;
    while(pmi != NULL){
        if (pmi->mi_Pid == pid){
            _disable();
            pmi->mi_fState = VDM_BEING_TERMINATED;
            _enable();
            return TRUE;
        }
        pmi = pmi->mi_pmi;
    }
    return FALSE;
}

/***LP+ pfnUndoHMAPID - release the MAPID data structure
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      SUCCESS - !0
 *      FAILURE - 0
 */
BOOL    PRIVENTRY pfnUndoHMAPID()
/*end*/
{
PMAPID        pmiCur,pmiLast;

    pmiCur = hmapid.hm_pmiFirst;
    pmiLast = NULL;
    while(pmiCur != NULL && pmiCur->mi_hvdm != vdmid_HVDM){
        pmiLast = pmiCur;
        pmiCur = pmiCur->mi_pmi;
    }

    if(pmiCur == NULL)
        return 0;
    else{
        _disable();
        // Removing from middle of list
        if(pmiLast != NULL)
            pmiLast->mi_pmi = pmiCur->mi_pmi;
        // Removing from the head of list
        else
            hmapid.hm_pmiFirst = pmiCur->mi_pmi;

        // Removing from tail of list
        if(pmiCur->mi_pmi == NULL)
            hmapid.hm_pmiLast = pmiLast;
        _enable();

        return 1;
    }
}

/***EP+ VDMMDispatchInContext - Dispatch out-of-context page faults
 *
 *      Dispatch the page fault to appropriate VDD-supplied page
 *      fault handler.  This is only called by VDMMDispatchPageFault
 *      when a page fault was taken out of context.  We use this
 *      routine to get to that task's context.  This is essential because
 *      VDDs have no way of servicing page faults out of context.
 *
 *      ENTRY
 *          pRefData - pointer to reference data
 *          pcrf     - pointer to client register frame
 *                     (NULL if called directly from VDMMDispatchPageFault)
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          VDM Task
 */

BOOL EXPENTRY VDMMDispatchInContext(register PPGFDATA ppgf, PCRF pcrf)
/*end*/
{
register PPFH ppfh;

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

//    PvwCnt_strict(vdm,TrapPage,0);                                   //6.16

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

    // Get the first page fault entry
    ppfh = vdmid_hpfh.hf_ppfhFirst;

    // Get the fault handler if any registered for pFault
    while (ppfh != NULL && (ULONG)ppfh->pf_pv + ppfh->pf_cPages*PAGESIZE
                           <= (ULONG)(ppgf->pFault))
        ppfh = ppfh->pf_ppfh;

    KSEMReleaseMutex (hksemHFault);

    if (ppfh != NULL && ppgf->pFault >= ppfh->pf_pv) {
        if (!(ppfh->pf_flags & VDHIFH_ADDR)) {
            (page_t)ppgf->pFault = (page_t)ppgf->pFault >> PAGESHIFT;
            (page_t)ppgf->pFault = (page_t)ppgf->pFault << PAGESHIFT;
        } /* endif */
        (*ppfh->pf_pfn)(ppgf->pFault);
    }
    // if no handler found look in the linear heap in following way:
    // if a record exist for this pFault and it is invalid, VDD is corrupted
    // else if a mapping is there then linear heap or Page Manager corrupted.
    // If there is no record then create one and map it to black hole.

    else
      switch(LHCheckForFault(ppgf->pFault)) {

        case RECORD_INVALID:// Record there but invalid
            VDHKillVDM (vdmid_HVDM);

        case RECORD_CREATED:// New record created and mapped to black holes
            break;
      }
    if (pcrf)
        KSEMPostEvent(ppgf->hksemFaultSync);
    return 0;
}

/***EP+ VDMMDispatchPageFault - Dispatch the page fault to its handler
 *
 *      Dispatch the page fault to appropriate VDD-supplied page
 *      fault handler.
 *
 *      CHANGED 28-Jul-89 (JeffPar) to correctly dispatch page faults even
 *      when a fault occurs out of the VDM's context.  One example is a
 *      protected-mode OS/2 thread making a DosRequestVDD call to a VDD that
 *      in turn must access one or more pages of some VDM's memory.  We
 *      will switch contexts to dispatch to the appropriate VDD page fault
 *      handle, and insure that it completes before returning back to the
 *      page manager.  A rarer example is a VDD touching VDM pages through
 *      the alias region (ie, HVDM);  whether we're in the same VDM context,
 *      a different VDM context, or (again) in a protected-mode thread's
 *      context, the page fault will be dispatched appropriately.  It also
 *      detects whether we are already in the proper context, to save the
 *      overhead of allocating/freeing a context hook whenever possible.  If
 *      there is not enough memory to allocate a context hook, it will spin
 *      for a time-slice and try again.  Context hooks are not preallocated,
 *      because we'd need one for every thread in the system, and one expects
 *      the allocation will never fail on a typical system anyway.
 *
 *      ENTRY
 *          paddr - byte granular faulting address
 *
 *      EXIT-SUCCESS
 *          returns 0, page fault was handled by VDMM
 *              caller should return to faulting instruction for retry
 *
 *      EXIT-FAILURE
 *          if no VDD is registered for handling fault at this address
 *          and currently this address is not being used in VDM then
 *          VDM Manager will map in the page to corresponding physical
 *          page.
 *
 *      CONTEXT
 *          Task
 *
 *      NOTES
 *          1) VMM will call here after making sure that the
 *          page in question is not swapped out. There are few
 *          important points here:
 *          - A page fault will always be handled in the context of the
 *            VDM which caused the page fault.
 *          - Two page fault handlers can not share the same linear
 *            region. This will be detected by VDHSetPageFaultHandler.
 *
 *          2) Preemption is possible between the time of page fault
 *          and the actual calling of the page fault handler. Similarly
 *          preemption is possible on quite many VDH services which
 *          this fault handler may call. So care should be taken
 *          (mostly by having a mutex semaphore) in fault handlers
 *          for non-VDM context exclusions.
 *
 *      PCODE
 *          Search the pfhTable for faulting laddr
 *          If found an entry
 *                  call the handler with faulting page address
 *                  return SUCCESS
 *          else terminate the VDM
 */

BOOL EXPENTRY VDMMDispatchPageFault(paddr)
PVOID         paddr;
/*end*/
{
PGFDATA       pgf;
PPGFDATA      ppgf;
HHOOK         hhook = NULL;
register      PMAPID        pmi;

    if ((ULONG)paddr >= ONEMBPLUS) {

        if (pPTDACur->ptda_infoseg.LIS_ProcType == LIS_PT_REALMODE)
            if ((ULONG)paddr - (ULONG)vdmid_HVDM < ONEMBPLUS) {
                paddr = (PVOID)( (ULONG)paddr - (ULONG)(vdmid_HVDM) );
                goto InContext;
            }
        pmi = hmapid.hm_pmiFirst;
        while (pmi != NULL) {
            if ((ULONG)paddr - (ULONG)(pmi->mi_hvdm) < ONEMBPLUS)
                break;
            pmi = pmi->mi_pmi;
        }
        if (!pmi)
            Panic(("VDMMDispatchPageFault:Page Fault not in VDMs"));

        // Allocate a context hook
        while (!hhook) {
            hhook = VDHAllocHook(VDH_CONTEXT_HOOK,
                                 (PFNARM)VDMMDispatchInContext,
                                 sizeof(PGFDATA));
            if (!hhook)
                TKSleep(paddr, 0, 0, 0);
        }
        ppgf = VDHQueryHookData(hhook);
        ppgf->pFault = (PVOID)( (ULONG)paddr - (ULONG)(pmi->mi_hvdm) );
        ppgf->hksemFaultSync = NULL;

        // Allocate an event sem
        while (!ppgf->hksemFaultSync) {
            KSEMCreate(&ppgf->hksemFaultSync,VDH_EVENTSEM);
            if (!ppgf->hksemFaultSync)
                TKSleep(paddr, 0, 0, 0);
        }

        KSEMResetEvent(ppgf->hksemFaultSync);
        VDHArmContextHook(hhook, pmi->mi_hvdm);
        KSEMWaitEvent(ppgf->hksemFaultSync, SEM_INDEFINITE_WAIT);
        VDHFreeHook(hhook);
        return 0;
    }
InContext:
    pgf.pFault = paddr;
    return VDMMDispatchInContext(SSToDS(&pgf), NULL);
}

/***EP+ VDMMstrncpyz -- strncpy with zero-termination
 *
 *      Copies up to cb-1 characters from pSrc to pDst, followed by
 *      a terminating 0.
 *
 *      ENTRY
 *          cb   -- size of destionation buffer
 *          pSrc -- source string
 *          pDst -- destination buffer
 *
 *      EXIT  None.
 *
 */

VOID EXPENTRY VDMMstrncpyz(cb, pSrc, pDst)
register ULONG cb;
register PSZ pSrc, pDst;
/*end*/
{

    if (cb) {
        while (--cb && (*pDst++ = *pSrc++))
            ;
        *pDst = '\0';
    }
}

#pragma END_MVDM_GLOBAL_CODE

#pragma BEGIN_MVDM_SWAP_CODE

#pragma code_seg("SWAP32_27","CODE")
/***LP+ pfnUndoLA - Release Linear Heap
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      None
 */
VOID    PRIVENTRY   pfnUndoLA()
/*end*/
{
PLR     plrCur,plrNext;

    plrCur = vdmid_hla->la_plrFirst;
    while(plrCur != NULL){
        plrNext = plrCur->lr_plr;
        VMFreeKHB(VM_HKH_PUB_RESRW,(PCHAR)plrCur);
        plrCur = plrNext;
    }
    VMFreeKHB(VM_HKH_PUB_RESRW,(PCHAR)vdmid_hla);
    return;
}

#pragma code_seg("SWAP32_27","CODE")
/***LP+ pfnUndoEM86 - Call EM86 to inform this VDM termination
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      NOne
 */
VOID    PRIVENTRY   pfnUndoEM86()
/*end*/
{
    EM86DestroyVDM();
    return;
}

#pragma code_seg("SWAP32_27","CODE")
/***LP+ pfnUndoVDD - Call each VDDs termination handler
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      NOne
 */
VOID    PRIVENTRY   pfnUndoVDD()
/*end*/
{
PEL           pel;

    // Call Each VDD registered with a VDM termination hook
    pel=aeh[VDM_TERMINATE].eh_pelFirst;
    while(pel != NULL){
        (*pel->el_pfn)(vdmid_HVDM);
        pel=pel->el_pel;
    }
    return;
}

#pragma code_seg("SWAP32_27","CODE")
/***LP+  pfnUndoFaults - Release the faults of the terminating VDM
 *
 *  ENTRY
 *     None
 *
 *  EXIT
 *      SUCCESS - returns !0
 *      FAILURE - returns 0
 *
 *  CONTEXT
 *      Terminating VDM
 *
 *  PCODE
 *
 */

VOID PRIVENTRY pfnUndoFAULTS()
/*end*/
{
PPFH    ppfhCur,ppfhNext;

    ppfhCur = vdmid_hpfh.hf_ppfhFirst;

    while(ppfhCur != NULL){
        ppfhNext = ppfhCur->pf_ppfh;
        VMFreeKHB(VM_HKH_PUB_RESRW,(PCHAR)ppfhCur);
        ppfhCur = ppfhNext;
    }
    return;
}

#pragma code_seg("SWAP32_27","CODE")
/***LP+ pfnUndoFixedHeap - release the data structures taken for fixed heap
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      None
 */
VOID    PRIVENTRY pfnUndoFixedHeap()
/*end*/
{
PHEAPBLOCK phbCur,phbNext;

    if(vdmid_phpFixed == NULL)
        return;
    phbCur  = vdmid_phpFixed->hp_phb;
    // Free selector for first entry
    if ((phbCur != NULL) && (phbCur->hb_sel != 0))
    {
      SELFreeGDT((SEL)phbCur->hb_sel);
    }
    while(phbCur != NULL){
        phbNext = phbCur->hb_phb;
        // Free selector for next entry if it is different, and
        // only if there is a next entry.
        if ((phbNext != NULL) && (phbCur->hb_sel != phbNext->hb_sel))
        {
          SELFreeGDT((SEL)phbNext->hb_sel);
        }
        VMFreeKHB(VM_HKH_PUB_RESRW,phbCur);
        phbCur = phbNext;
    }
    VMFreeKHB(VM_HKH_PUB_RESRW,vdmid_phpFixed);
    return;
}


#pragma code_seg("SWAP32_27","CODE")
/***LP+ pfnUndoSwapHeap - release the data structures taken for swap heap
 *
 *  ENTRY
 *      None
 *
 *  EXIT
 *      None
 */
VOID    PRIVENTRY pfnUndoSwapHeap()
/*end*/
{
PHEAPBLOCK phbCur,phbNext;

    if(vdmid_phpSwap == NULL)
        return;
    phbCur  = vdmid_phpSwap->hp_phb;
    // Free selector for first entry
    if ((phbCur != NULL) && (phbCur->hb_sel != 0))
    {
      SELFreeGDT((SEL)phbCur->hb_sel);
    }
    while(phbCur != NULL){
        phbNext = phbCur->hb_phb;
        // Free selector for next entry if it is different, and
        // only if there is a next entry.
        if ((phbNext != NULL) && (phbCur->hb_sel != phbNext->hb_sel))
        {
          SELFreeGDT((SEL)phbNext->hb_sel);
        }
        VMFreeKHB(VM_HKH_PUB_RESRW,phbCur);
        phbCur = phbNext;
    }
    VMFreeKHB(VM_HKH_PUB_RESRW, vdmid_phpSwap);
    return;
}


#pragma code_seg("SWAP32_27","CODE")
/***EP+ VDMMSwitchBgnd - Switch VDM screen group to background
 *
 *      This routine receives control from w_DosSGSwitch, which is
 *      called by the Session Manager whenever a screen switch is
 *      initiated.  This encapsulates the MVDM-dependent portion of
 *      background screen-switching.
 *
 *      ENTRY
 *          uSG - outgoing VDM screen group
 *
 *      EXIT-SUCCESS
 *          None.
 *
 *      EXIT-FAILURE
 *          Internal Error (screen group is not VDM)
 *
 *      CONTEXT
 *          Task
 *
 *      NOTES
 *          It is assumed that the SG ID in the global infoseg
 *          still retains the outgoing foreground screen group #,
 *          so that VDDs are allowed to make this assumption.
 *
 *      PSEUDO-CODE
 *          get the linked list of handlers for background
 *          call vdmmProcFBgnd
 */

VOID EXPENTRY VDMMSwitchBgnd(uSG)
USHORT uSG;
/*end*/
{

       vdmmProcFBgnd(uSG,SWITCH_BGND);
       return;
}

#pragma code_seg("SWAP32_27","CODE")
/***EP+ VDMMSwitchFgnd - Switch VDM screen group to foreground
 *
 *      This routine receives control from w_DosSGSwitch, which is
 *      called by the Session Manager whenever a screen switch is
 *      initiated.  This encapsulates the MVDM-dependent portion of
 *      foreground screen-switching.
 *
 *      ENTRY
 *          uSG - incoming VDM screen group
 *
 *      EXIT-SUCCESS
 *          None.
 *
 *      EXIT-FAILURE
 *          Internal Error (screen group is not VDM)
 *
 *
 *      CONTEXT
 *          Task
 *
 *      NOTES
 *          It is assumed that the SG ID in the global infoseg
 *          now contains the incoming foreground screen group #,
 *          so that VDDs are allowed to make this assumption.
 *
 *      PSEUDO-CODE
 *          get the linked list of handlers for foreground
 *          call vdmmProcFBgnd
 */

VOID EXPENTRY VDMMSwitchFgnd(uSG)
USHORT uSG;
/*end*/
{

       vdmmProcFBgnd(uSG,SWITCH_FGND);
       return;
}

#pragma code_seg("SWAP32_18","CODE")
/***LP+  vdmmProcFBgnd - Process the foreground/background switching
 *
 *      vdmmProcFBgnd is the the generic proc to do foreground/background
 *      switching.
 *
 *      ENTRY
 *          uSG - USHORT
 *          fSwitch - Background/Foreground
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          Get HVDM from Screen Group ID
 *          Set pointer to foreground or background notification list
 *          Find MAPID entry for HVDM
 *          If VDM is not in the process of being terminated
 *             While event list is not null
 *                 call the handler
 *          return
 */

VOID PRIVENTRY vdmmProcFBgnd(uSG,fSwitch)
USHORT uSG;
BOOL   fSwitch;
/*end*/
{
register PEL  pel;
register HVDM hvdm;
register PMAPID pmi;

       // mask off any high bits left set by DosSGSwitch()
       uSG &= 0xff;

       // Get HVDM from Screen Group ID
       hvdm=GetVDMHandle(uSG,0,FALSE);

       // If VDM has died just return because session manager
       // does'nt know yet, that VDM has died.
       if(hvdm == (HVDM)0)
            return;

       // Set pointer to foreground or background notification list
       if(fSwitch == SWITCH_BGND)
       {
            pel=aeh[VDM_BACKGROUND].eh_pelFirst;
            REFHVDM(hvdm,BOOL,vdmid_fFgnd) = VDM_BGND;
       }
       else
       {
            pel=aeh[VDM_FOREGROUND].eh_pelFirst;
            REFHVDM(hvdm,BOOL,vdmid_fFgnd) = VDM_FGND;
       }

       // Set up the pointer to mapid structure for respective hvdm.
       // This will allow checking to see if VDM is being terminated.
       pmi=hmapid.hm_pmiFirst;
       while(pmi != NULL)
       {
           if(pmi->mi_hvdm == hvdm)
           {
              break;
           }
           pmi=pmi->mi_pmi;
       }

       // If VDM is being terminating then skip this VDM notification
       if (pmi != NULL)
       {
          if(pmi->mi_fState != VDM_BEING_TERMINATED)
          {
             while(pel != NULL)
             {
                  (*pel->el_pfn)(hvdm);
                  pel=pel->el_pel;
             }
          }
       }
       return;
}

#pragma code_seg("SWAP32_20","CODE")
/***EP+ VDMMPDBChange - PDB switch notification
 *
 *      This routine receives control from demSetCurrentPDB.
 *      It notifies all VDDs hooked on VDM_PDB_CHANGE
 *
 *      ENTRY
 *          segPDB - v86 segment of PDB
 *
 *      EXIT
 *          None.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      PSEUDO-CODE
 *          call vdmmProcPDB
 */

VOID EXPENTRY VDMMPDBChange(segPDB)
ULONG segPDB;
/*end*/
{
    vdmmProcPDB(segPDB,PDB_CHANGE);
    return;
}

#pragma code_seg("SWAP32_27","CODE")
/***EP+ VDMMPDBDestroy - PDB termination notification
 *
 *      This routine receives control from demSetCurrentPDB.
 *      It notifies all VDDs hooked on VDM_PDB_CHANGE
 *
 *      ENTRY
 *          segPDB - v86 segment of PDB
 *
 *      EXIT
 *          None.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      PSEUDO-CODE
 *          call vdmmProcPDB
 */

VOID EXPENTRY VDMMPDBDestroy(segPDB)
ULONG segPDB;
/*end*/
{
    vdmmProcPDB(segPDB,PDB_DESTROY);
    return;
}
#pragma code_seg("SWAP32_20","CODE")
/***LP+  vdmmProcPDB - Process the PDB switch/destroy notifications
 *
 *      vdmmProcPDB is the the generic proc to do switch/destroy
 *      processing.
 *
 *      ENTRY
 *          segPDB - PDB segment
 *          fEvent - switch/destroy
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          While event list is not null
 *              call the handler
 *          return
 */

VOID PRIVENTRY vdmmProcPDB(segPDB,fEvent)
ULONG   segPDB;
BOOL    fEvent;
{
register PEL  pel;

    if (fEvent == PDB_CHANGE)
        pel = aeh[VDM_PDB_CHANGE].eh_pelFirst;
    else
        pel = aeh[VDM_PDB_DESTROY].eh_pelFirst;
    while (pel != NULL) {
         ((PPDBH)*pel->el_pfn)(vdmid_HVDM,(USHORT)segPDB);
         pel=pel->el_pel;
    }
    return;
}


#pragma code_seg("SWAP32_19","CODE")
/***EP+ VDMMMemoryMappedIn - Memory mapped in notification
 *
 *      This routine receives control from VDHMapPages.
 *      It notifies all VDDs hooked on VDM_MEMORY_MAPPED_IN.
 *
 *      ENTRY
 *          page - page address
 *          cpages - #pages mapped in from the starting
 *                   page address
 *          fl - mapping flag
 *
 *      EXIT
 *          None.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      PSEUDO-CODE
 *          call vdmmProcMemMapped
 */

VOID EXPENTRY VDMMMemoryMappedIn(page,cpages,fl)
page_t page;
INT cpages;
ULONG fl;
/*end*/
{
    vdmmProcMemMapped(page,cpages,fl,VDM_MEMORY_MAPPED_IN);
    return;
}

#pragma code_seg("SWAP32_19","CODE")
/***EP+ VDMMMemoryUnMapped - Memory unmapped notification
 *
 *      This routine receives control from em86, ........
 *      It notifies all VDDs hooked on VDM_MEMORY_UN_MAPPED.
 *
 *      ENTRY
 *          page - page address
 *          cpages - #pages unmapped from the starting
 *                   page address
 *          fl - mapping flag
 *
 *      EXIT
 *          None.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      PSEUDO-CODE
 *          call vdmmProcMemMapped
 */

VOID EXPENTRY VDMMMemoryUnMapped(page,cpages,fl)
page_t page;
INT cpages;
ULONG fl;
/*end*/
{
    vdmmProcMemMapped(page,cpages,fl,VDM_MEMORY_UN_MAPPED);
    return;
}


#pragma code_seg("SWAP32_19","CODE")
/***LP+  vdmmProcMemMapped - Process the memory mapped/unmapped notifications
 *
 *      vdmmProcMemMapped is the generic proc to do memory
 *      mapped/unmapped notification processing.
 *
 *      ENTRY
 *          page   - beginning page address of mapped/unmapped area
 *          cpages - count of pages mapped/unmapped
 *          fl     - type of mapping requested on VDH call
 *          fEvent - VDM_MEMORY_MAPPED_IN or VDM_MEMORY_UN_MAPPED
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          Set event list head depending on fEvent
 *          While the event list is not null
 *            call the handler
 *          return
 */

VOID PRIVENTRY vdmmProcMemMapped(page,cpages,fl,fEvent)
page_t page;
INT cpages;
ULONG fl;
BOOL    fEvent;
{
register PEL  pel;

    if (fEvent == VDM_MEMORY_MAPPED_IN)
        pel = aeh[VDM_MEMORY_MAPPED_IN].eh_pelFirst;
    else
        pel = aeh[VDM_MEMORY_UN_MAPPED].eh_pelFirst;
    while (pel != NULL) {
         ((PMEMMAPPEDH)*pel->el_pfn)(vdmid_HVDM,page,cpages,fl);
         pel=pel->el_pel;
    }
    return;
}

#pragma code_seg("SWAP32_23","CODE")
/***LP+  vdmmProcCreateDone - Process the VDM Create Done notifications
 *
 *      vdmmProcCreateDone is the proc to notify VDDs who have
 *      registered to receive notification of the VDM being successfully
 *      created.
 *
 *      ENTRY
 *          None
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          Set event list head
 *          While the event list is not null
 *            call the handler
 *          return
 */

VOID PRIVENTRY vdmmProcCreateDone(VOID)
{
register PEL  pel;

    // Set event list head
    pel = aeh[VDM_CREATE_DONE].eh_pelFirst;

    // While the event list is not null
    //   call the handler
    while (pel != NULL) {
         ((PUH)*pel->el_pfn)(vdmid_HVDM);
         pel=pel->el_pel;
    }
    return;
}
#pragma code_seg("SWAP32_25","CODE")
/***EP+ VDMMVPMBeginEnd - VPM task begin/end notification               // 1.3
 *
 *      This routine receives control from em86, when a Virtual Protected
 *      Mode (VPM) task initially enters protect mode, and is also notified
 *      when the VPM task terminates.
 *
 *      ENTRY
 *          fEvent - VDM_BEGIN_VPM_TASK or VDM_END_VPM_TASK
 *
 *      EXIT
 *          None.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      PSEUDO-CODE
 *          Set event list head depending on fEvent
 *          While the event list is not null
 *            call the handler
 *          return
 */

VOID EXPENTRY VDMMVPMBeginEnd(fEvent)                                   // 1.3
ULONG fEvent;
/*end*/
{
register PEL  pel;

    if (fEvent == VDM_BEGIN_VPM_TASK)
        pel = aeh[VDM_BEGIN_VPM_TASK].eh_pelFirst;
    else
        pel = aeh[VDM_END_VPM_TASK].eh_pelFirst;
    while (pel != NULL) {
         (*pel->el_pfn)(vdmid_HVDM);
         pel=pel->el_pel;
    }
    return;
}

#pragma END_MVDM_SWAP_CODE
