/*static char *SCCSID = "@(#)vmapi.c    6.6 92/01/06";*/
#define SCCSID  "@(#)vmapi.c    6.6 92/01/06 "
/*
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 * 72417  12/22/95  MLM - Moved find.h after tkexport.h since most dirent
 *                        stuff is now in tkexport.h
 * 150219 04/02/96  TPL - Fail any hybernation function calls on a secured
 *                        system.  Use the fSec variable as the indicator.
 * 150806 04/05/96  TPL - Use strategy command to replace APM notification
 * 150281 04/11/96  TPL - Pass freeze/thaw flag to hyberFreezeSys
 * 151783 04/11/96 @V3.2SEN00  - Add int 10 support
 * 152533 04/23/96  TPL - Check fhybernation flag instead of fSec flag
 * 152914 05/02/96  RAM - Add processor type and IOdelay count to DOSSYSCTL
 * 153480 05/10/96  TPL - Add vmFreeAllContext to DOSSYSCTL to support
 *                        freeing of shared memory from every user context
 * 147968 05/11/96  RJM - Change FSD and DD system owners to use HMTEs
 * 156773 06/14/96  TPL - Remove SWAPPER2.DAT dependency
 */

#define INCL_TYPES                      /* for basedef.h */
#define INCL_ERRORS
#include <basedef.h>
#include <bseerr.h>
#include <os2defp.h>
#include <bsememf.h>
#include <bsedosp.h>

#define  FNO_NUM FNO_VMAPI_C
#include <fnomem.h>
#include <osmac.h>

#include <mi.h>
#include <vmowner.h>
#include <infoseg.h>
#include <dossymc.h>
#include <curdir.h>
#include <dirent.h>
#include <screen.h>
#include <sigdef.h>
#include <except.h>
#include <sem.h>
#include <ioexp.h>
#include <pgexport.h>
#include <vmexport.h>
#include <vmlocal.h>
#include <selxport.h>
#include <tkexport.h>
#include <find.h>                       // 72417 moved after tkexport.h
#include <tkptda.h>
#include <ldrxport.h>
#include <kmode.h>
#include <mmph.h>

#include <mvdmexp.h>
#include <smexport.h>
#include <pglocal.h>
#define FOR_EXEHDR      1 /*147968 */
#include <exe386.h>       /*147968 */
#include <ldrvars.h>      /*147968 */

/*
 *  PG_DECOMMITABLE - This is an undocumented bit for the DosAllocSharedMem API
 *  it allows users the ability to decommit shared memory, similiar to the
 *  "shrinkable bit" it 1.3
 */
#define  PG_DECOMMITABLE   0x800


extern PPTDA pPTDAFirst ;

uchar_t smDelaySwap;
extern ushort_t twodiskboot;
extern char smInitialized;
extern void SMReinit();
extern ulong_t hyberStat;


#pragma data_seg("CSWAP_DATA", "CSWAPDATA")

/*
 * Api to VM/PG flag translation tables.
 */
int     vmApiF0[16] = {
    0,
    PG_R,                       /* PAG_READ */
    PG_W+PG_R,                  /* PAG_WRITE */
    PG_W+PG_R,                  /* PAG_WRITE+PAG_READ */
    PG_X,                       /* PAG_EXECUTE */
    PG_X+PG_R,                  /* PAG_EXECUTE+PAG_READ */
    PG_X+PG_W+PG_R,             /* PAG_EXECUTE+PAG_WRITE */
    PG_X+PG_W+PG_R,             /* PAG_EXECUTE+PAG_WRITE+PAG_READ */
    PG_GUARD,                   /* PAG_GUARD */
    PG_GUARD+PG_R,              /* PAG_GUARD+PAG_READ */
    PG_GUARD+PG_W+PG_R,         /* PAG_GUARD+PAG_WRITE */
    PG_GUARD+PG_W+PG_R,         /* PAG_GUARD+PAG_WRITE+PAG_READ */
    PG_GUARD+PG_X,              /* PAG_GUARD+PAG_EXECUTE */
    PG_GUARD+PG_X+PG_R,         /* PAG_GUARD+PAG_EXECUTE+PAG_READ */
    PG_GUARD+PG_X+PG_W+PG_R,    /* PAG_GUARD+PAG_EXECUTE+PAG_WRITE */
    PG_GUARD+PG_X+PG_W+PG_R     /* PAG_GUARD+PAG_EXECUTE+PAG_WRITE+PAG_READ */
};

int     vmApiF1[16] = {
    0,
    0,                          /* PAG_COMMIT */
    VMS_DECOMMIT,               /* PAG_DECOMMIT */
    VMS_DECOMMIT,               /* PAG_DECOMMIT+PAG_COMMIT */
    VMAC_SELALLOC,              /* OBJ_TILE */
    VMAC_SELALLOC,              /* OBJ_TILE+PAG_COMMIT */
    VMAC_SELALLOC+VMS_DECOMMIT, /* OBJ_TILE+PAG_DECOMMIT */
    VMAC_SELALLOC+VMS_DECOMMIT, /* TILE+DECOMMIT+COMMIT */
    VMAC_PROT,                  /* OBJ_PROTECTED */
    VMAC_PROT,                  /* OBJ_PROTECTED+PAG_COMMIT */
    VMAC_PROT+VMS_DECOMMIT,     /* OBJ_PROTECTED+PAG_DECOMMIT */
    VMAC_PROT+VMS_DECOMMIT,     /* PROTECTED+DECOMMIT+COMMIT */
    VMAC_PROT+VMAC_SELALLOC,    /* PROTECTED+TILE */
    VMAC_PROT+VMAC_SELALLOC,    /* PROTECTED+TILE+COMMIT */
    VMAC_PROT+VMAC_SELALLOC+VMS_DECOMMIT,/* PROTECTED+TILE+DECOMMIT */
    VMAC_PROT+VMAC_SELALLOC+VMS_DECOMMIT /* PROT+TILE+DECOMMIT+
                                                        COMMIT */
};

/* default SELMGR flags */

#define fldef (SELAL_SELMAP | SELAL_W | (3 << SELAL_PRIVSHIFT))

int     vmAPIF2[4] = {              /* Alias API flags */
    fldef,
    fldef | SELAL_CODE,
    fldef | SELAL_32BIT,
    fldef | SELAL_32BIT | SELAL_CODE
};

#pragma data_seg()

/***LP  vmfTranslate - Translate API flags to internal VM/PG flags.
 *
 *      vmfTranslate performs a table lookup to translate the API flags
 *      into appropriate VM and PG flags.
 *
 *      Note: PAG_DEFAULT, OBJ_GETTABLE and OBJ_GIVEABLE are zeroed and
 *      not translated.
 *
 *      (vmApiF0[(fl) & 0xF] + vmApiF1[((fl) >> 6) & 0xF]);
 */

#define vmfTranslate(fl) (vmApiF0[(fl) & 0xF] + vmApiF1[((fl) >> 4) & 0xF])

#if     NO_ERROR
.error  NO_ERROR is not 0
#endif



/**************************************************************************
 *                                                                        *
 *  WARNING: Everything below this line will be in swappable code!!!!     *
 *                                                                        *
 **************************************************************************/

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


/***EP  VR32AliasMem - Create an alias to a memory object.
 *
 *      VR32AliasMem is the Dos32AliasMem validation routine.  Arguments
 *      are validated and control is transfered to the worker routine.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              addr - page aligned address in the object to alias
 *              size - size of region to alias
 *              palias - pointer to location in user space to receive alias
 *                       address
 *              flags - Flags indicating alias type
 *      RETURN  0 on success, else error code
 *                *alias - alias's base address
 */

int cdecl
VR32AliasMem(sef, addr, size, palias, flags)
THUNKSEF sef;
ulong_t addr;
ulong_t size;
ulong_t *palias;
ulong_t flags;
{
    ulong_t aliasL;
    ulong_t rc;

    /* PTR: B725695
     * Validate palias before it's used (MMIOPH kernel only).
     * Since this address may be used by the hook and since
     * hooks execute at the CPL (current protection level)
     * we could trap inside the kernel.
     */
    #ifdef  MMIOPH
       if(TKProbeBuff((char *)palias,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
    #endif

    /*
     * Address must be page aligned.
     */
    if ((addr & PAGEMASK) || addr >= MAXCRLADDR || addr < MINCRLADDR) {
        return(ERROR_INVALID_ADDRESS);
    }

    /*
     * Call worker and pass alias address back to user if no error.
     */
    if ((rc = VM32ApiAliasMem(addr, size, SSToDS(&aliasL), flags)) == 0) {
        /* PTR: B725695
         * Use TKSuDWordNF since palias may have
         * been invalidated. If so release the
         * memory and return an error.
         */
        if(TKSuDWordNF(aliasL, palias)) {
           rc = VMFreeMem(aliasL,HOBNULL,0);
           vmAssert(rc==0);
           rc = ERROR_INVALID_PARAMETER;
        }
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    return(rc);
}

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

/***EP  VR32AllocMem - Allocate a memory object in the private arena.
 *
 *      VR32AllocMem is the Dos32AllocMem validation routine.  Arguments
 *      are validated and control is transfered to the worker routine.
 *
 *      VM32ApiAllocMem is the worker routine for the Dos32AllocMem api.
 *      This api allocates an object of the specified size in the private
 *      arena.  If the request is for a tiled object, as many selectors as
 *      required to map the entire object are allocated as well.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   paddr - pointer to location in user space to receive object
 *                      address
 *              size - size of object to allocate
 *              flags - allocation flags
 *                  OBJ_TILE        allocate in tiled arena
 *                  PAG_COMMIT      commit all pages
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *                  OBJ_SELMAPALL   first selector map all
 *      RETURN  0 on success, else error code
 *                *addr - object's base address
 */

ulong_t cdecl
VR32AllocMem(sef, paddr, size, flags)
THUNKSEF sef;
ulong_t *paddr;
ulong_t size;
ulong_t flags;
{
    ulong_t rc;
    ulong_t szCommit;
    VMAC ac;
    ushort_t selflags;
    VMHOB creatingMTE;

    /* PTR: B725695
     * Validate paddr before it's used (MMIOPH kernel only).
     * Since this address may be used by the hook and since
     * hooks execute at the CPL (current protection level)
     * we could trap inside the kernel.
     */
    #ifdef  MMIOPH
       if(TKProbeBuff((char *)paddr,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
    #endif

    MMPHPreVM32ApiAllocMem()                    /* Performance Hook (20/19) */

    /*
     * Validate flags and size.  (Note that size validation is done to avoid
     * rounding to 0 in VM.)
     */
    if ((flags & ~(fALLOC|PAG_GUARD|OBJ_SELMAPALL)) || (flags & fPERM) == 0) {
        MMPHPostVM32ApiAllocMem()               /* Performance Hook (20/99) */
        return(ERROR_INVALID_PARAMETER);
    }

    if (size > VMMAXSIZE) {
        MMPHPostVM32ApiAllocMem()               /* Performance Hook (20/99) */
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    /*
     * Size must be > 0.  Round the size up to multiple of page size.
     */
    size = (size + PAGEMASK) & ~PAGEMASK;
    if (size == 0) {
        MMPHPostVM32ApiAllocMem()               /* Performance Hook (20/99) */
        return(ERROR_INVALID_PARAMETER);
    }

    /*
     * Either entire object is committed, or none of it is committed.
     */
    szCommit = (flags & PAG_COMMIT) ? size : 0;

    /*
     * Set selector manager flags.
     */
    selflags = (ushort_t)(SELAL_SELMAP | (3 << SELAL_PRIVSHIFT) |
               ((flags & PAG_WRITE) ? SELAL_W : 0) |
               ((flags & OBJ_SELMAPALL) ? SELAL_32BIT | SELAL_BASEALL : 0));

    /*
     * Parameter validation is complete, so translate the flags arguments.
     */
    flags = vmfTranslate(flags & ~OBJ_SELMAPALL);

    /*
     * Set additional VM and PG flags.  Force all allocations to be tiled.
     */
    flags |= VMAC_ALIGNSEL + VMAC_SELALLOC + VMAC_LOCANY + VMAC_ARENAPRV +
             VMAC_OBAPI + PG_ZEROFILL + PG_SWAPPABLE + PG_U;

    /*
     * Set selector manager flags.
     */
    selflags = (ushort_t)(SELAL_SELMAP + (3 << SELAL_PRIVSHIFT) +
               ((flags & PG_W) ? SELAL_W : 0));

    /*
     * Figure out to whom to attribute the memory.
     */
    creatingMTE =VMGetOwner(sef.thunksef_sef.sef_cs,sef.thunksef_sef.sef_eip);

    /*
     * Allocate the memory.
     */
    if ((rc = VMAllocMem(size,          /* size in bytes */
                         szCommit,      /* commit size */
                         flags,         /* allocation flags */
                         HOBNULL,       /* ptda handle */
                         HOBNULL,       /* owner */
                         creatingMTE,   /* mte handle */
                         selflags,      /* selector manager flags */
                         0,             /* loader block number */
                         SSToDS(&ac))) == NO_ERROR) { /* return buffer */
        /* PTR: B725695
         * Use TKSuDWordNF since paddr may have
         * been invalidated. If so release the
         * memory and return an error.
         */
        if(TKSuDWordNF((ulong_t)ac.ac_va, paddr)) {
           rc = VMFreeMem(ac.ac_va,HOBNULL,0);
           vmAssert(rc==0);
           rc = ERROR_INVALID_PARAMETER;
        }
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    MMPHPostVM32ApiAllocMem()                   /* Performance Hook (20/99) */
    return(rc);
}

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

/***EP  VR32AllocSharedMem - Allocate a memory object in the shared arena.
 *
 *      VR32AllocSharedMem is the Dos32AllocSharedMem validation routine.
 *      Arguments are validated and control is transfered to the worker
 *      routine.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              paddr - ptr to location in user space to store object address
 *              pname - optional pointer to name string to associate with the
 *                      object.
 *              size - size of object to allocate
 *              flags - allocation flags
 *                  OBJ_TILE        allocate in tiled arena
 *                  OBJ_GIVEABLE    giveable to other tasks
 *                  OBJ_GETTABLE    gettable from other tasks
 *                  OBJ_FORK        inheritable on posix fork
 *                  OBJ_EXECUTE     inheritable on posix execute
 *                  PAG_COMMIT      commit all pages
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *      RETURN  0 on success, else error code
 */

int cdecl
VR32AllocSharedMem(sef, paddr, pname, size, flags)
THUNKSEF sef;
ulong_t *paddr;
char *pname;
ulong_t size;
ulong_t flags;
{
    ulong_t addrL;
    ulong_t rc;

    /* PTR: B725695
     * Validate paddr & pname before they're used (MMIOPH kernel only).
     * Since these addresses may be used by the hook and since
     * hooks execute at the CPL (current protection level)
     * we could trap inside the kernel.
     */
    #ifdef  MMIOPH
       int DummyLen;  /* Used to validate pname */
       if(TKProbeBuff((char *)paddr,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
       if(pname)
         if(TKFuBuffLenz(SSToDS(&DummyLen),(char *)pname,MAXPATHLEN,0))
            return(ERROR_INVALID_PARAMETER);
    #endif

    /*
     * Validate flags and size.
     */
//    if ((flags & ~(fALLOCSHR|PAG_GUARD)) ||   D# 52259
    if ((flags & ~(fALLOCSHR|PAG_GUARD|PG_DECOMMITABLE)) ||
        (flags & fPERM) == 0 ||
        pname && (flags & (OBJ_GETTABLE|OBJ_GIVEABLE)) ||
        !pname && (flags & (OBJ_GETTABLE|OBJ_GIVEABLE)) == 0) {
        return(ERROR_INVALID_PARAMETER);
    }

    if (size > VMMAXSIZE) {
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    /*
     * Call worker and pass address back to user if no error
     */
    if ((rc = VM32ApiAllocSharedMem(SSToDS(&addrL), pname, size, flags, SSToDS(&sef))) == 0) {
        /* PTR: B725695
         * Use TKSuDWordNF since paddr may have
         * been invalidated. If so release the
         * memory and return an error.
         */
        if(TKSuDWordNF(addrL, paddr)) {
           rc = VMFreeMem(addrL,HOBNULL,0);
           vmAssert(rc==0);
           rc = ERROR_INVALID_PARAMETER;
        }
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    return(rc);
}

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

/***EP  VR32AllocProtectedMem - Allocate a protected memory object
 *
 *      VR32AllocProtectedMem is the Dos32AllocProtectedMem validation routine.
 *      This api allocates an object from the protected region of the shared
 *      arena.  Arguments are validated and control is transfered to the worker
 *      common to VR32AllocProtectedMem and VR32AllocSharedMem.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              paddr - ptr to location in user space to store object address
 *              pname - optional pointer to name string to associate with the
 *                      object.
 *              size - size of object to allocate
 *              flags - allocation flags
 *                  OBJ_TILE        allocate in tiled arena
 *                  OBJ_GIVEABLE    giveable to other tasks
 *                  OBJ_GETTABLE    gettable from other tasks
 *                  OBJ_FORK        inheritable on posix fork
 *                  OBJ_EXECUTE     inheritable on posix execute
 *                  PAG_COMMIT      commit all pages
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *      RETURN  0 on success, else error code
 */

int cdecl
VR32AllocProtectedMem(sef, paddr, pname, size, flags)
THUNKSEF sef;
ulong_t *paddr;
char *pname;
ulong_t size;
ulong_t flags;
{
    ulong_t addrL;
    ulong_t rc;

    /* PTR: B725695
     * Validate paddr & pname before they're used (MMIOPH kernel only).
     * Since these addresses may be used by the hook and since
     * hooks execute at the CPL (current protection level)
     * we could trap inside the kernel.
     */
    #ifdef  MMIOPH
       int DummyLen;  /* Used to validate pname */
       if(TKProbeBuff((char *)paddr,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
       if(pname)
         if(TKFuBuffLenz(SSToDS(&DummyLen),(char *)pname,MAXPATHLEN,0))
            return(ERROR_INVALID_PARAMETER);
    #endif

    /*
     * Validate flags and size.
     */
    if ((flags & ~(fALLOCSHR|PAG_GUARD)) ||
        (flags & fPERM) == 0 ||
        pname && (flags & (OBJ_GETTABLE|OBJ_GIVEABLE))) {
        return(ERROR_INVALID_PARAMETER);
    }

    if (size > VMMAXSIZE) {
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    /*
     * Call worker and pass address back to user if no error
     */
    if ((rc = VM32ApiAllocSharedMem(SSToDS(&addrL), pname, size,
        flags | OBJ_PROTECTED, SSToDS(&sef))) == 0) {
        /* PTR: B725695
         * Use TKSuDWordNF since paddr may have
         * been invalidated. If so release the
         * memory and return an error.
         */
        if(TKSuDWordNF(addrL, paddr)) {
           rc = VMFreeMem(addrL,HOBNULL,0);
           vmAssert(rc==0);
           rc = ERROR_INVALID_PARAMETER;
        }
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    return(rc);
}

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

/***EP  VR32GetNamedSharedMem - Attach to existing named shared memory object.
 *
 *      VR32GetNamedSharedMem is the Dos32GetNamedSharedMem validation routine.
 *      Arguments are validated and control is transfered to the worker
 *      routine.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              paddr - ptr to location in user space to store object address
 *              pname - ptr to name string associated with the object
 *              flags - attribute flags
 *                  OBJ_FORK        inheritable on posix fork
 *                  OBJ_EXECUTE     inheritable on posix execute
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *      RETURN  0 on success, else error code
 */

int cdecl
VR32GetNamedSharedMem(sef, paddr, pname, flags)
THUNKSEF sef;
ulong_t *paddr;
char *pname;
ulong_t flags;
{
    ulong_t addrL;
    ulong_t rc;

    /* PTR: B725695
     * Validate paddr & pname before they're used (MMIOPH kernel only).
     * Since these addresses may be used by the hook and since
     * hooks execute at the CPL (current protection level)
     * we could trap inside the kernel.
     */
    #ifdef  MMIOPH
       int DummyLen;  /* Used to validate pname */
       if(TKProbeBuff((char *)paddr,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
       if(TKFuBuffLenz(SSToDS(&DummyLen),(char *)pname,MAXPATHLEN,0))
          return(ERROR_INVALID_PARAMETER);
    #endif

    /*
     * Validate flags.
     */
    if ((flags & fPERM) == 0 || (flags & ~(fGETNMSHR|PAG_GUARD))) {
        return(ERROR_INVALID_PARAMETER);
    }

    /*
     * Call worker and pass address back to user if no error.
     */
    if ((rc = VM32ApiGetNamedSharedMem(SSToDS(&addrL), pname, flags)) == 0) {
        /* PTR: B725695
         * Use TKSuDWordNF since paddr may have
         * been invalidated. If so release the
         * memory and return an error.
         */
        if(TKSuDWordNF(addrL, paddr)) {
           rc = VMFreeMem(addrL,HOBNULL,0);
           vmAssert(rc==0);
           rc = ERROR_INVALID_PARAMETER;
        }
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    return(rc);
}

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

/***EP  VR32GetSharedMem - Attach to existing shared memory object.
 *
 *      VR32GetSharedMem is the Dos32GetSharedMem validation routine.
 *      Arguments are validated and control is transfered to the worker
 *      routine.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              addr - base address of object to attach
 *              flags - attribute flags
 *                  OBJ_FORK        inheritable on posix fork
 *                  OBJ_EXECUTE     inheritable on posix execute
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *      RETURN  0 on success, else error code
 */

int cdecl
VR32GetSharedMem(sef, addr, flags)
THUNKSEF sef;
ulong_t addr;
ulong_t flags;
{

    struct ptda_s *pptda = NULL;
    VMOD od;
    VMHOB hob;
    ulong_t rc;

    MMPHPreVM32ApiGetSharedMem()                /* Performance Hook (20/1C) */

    /*
     * Validate flags.
     */
    if ((flags & fPERM) == 0 || (flags & ~(fGETSHR|PAG_GUARD))) {
        return(ERROR_INVALID_PARAMETER);
    }

    /*
     * Address must be page aligned.
     */
    if ((addr & PAGEMASK) || addr >= MAXCRLADDR || addr < MINCRLADDR) {
        return(ERROR_INVALID_ADDRESS);
    }

    /*
     * Call worker.
     */
//    return(VM32ApiGetSharedMem(addr, flags));

    /*
     * Find a context that has the object mapped in to its arena, and get
     * the object's handle semaphore.  Note that if we block while
     * trying to take the semaphore,  VMGetOd will automatically restart
     * and repeat until the semaphore can be taken without blocking.  Thus
     * the final target context found (if any) is prevented from freeing
     * its context.  Verify that the requested address is the object's
     * base.
     */

    od.od_laddr = addr;
    od.od_pPTDA = NULL;
    if((rc = VMGetOd(VMOD_ANY_CTXT| VMGS_TAKESEM, SSToDS(&od))) !=
         NO_ERROR)
      {
        MMPHPostVM32ApiGetSharedMem()           /* Performance Hook (20/9C) */
        return(ERROR_INVALID_ADDRESS);
      }

    /*
     * Verify that the linear address of the object found matches the
     * given address.
     */

    hob = VMHobOd(od);
    if(addr != od.od_laddr)
      {
        rc = ERROR_INVALID_ADDRESS;
        goto retclearsem;
      }

    /*
     *  Verify that the object has shared data and is gettable.
     */

    if(!VMShrContOd(od) ||
      (VMHobOwnerOd(od) != GETSEGOWNER && VMHobOwnerOd(od) != SHRDOWNER))
      {
        rc = ERROR_ACCESS_DENIED;
        goto retclearsem;
      }

    vmAssert(VMShrArena(addr));

    /*
     *  Translate flags.
     */

    flags = vmfTranslate(flags) + PG_U;

    /*
     *  Do the attachment.
     */

    rc = VMAttach(hob, pPTDACur->ptda_handle, flags);

    /*
     *  Clear handle semaphore and return.
     */

retclearsem:

    VMClearSem(hob);
    vmAssert(pTSDCur->TSDcObjSem == 0);
        MMPHPostVM32ApiGetSharedMem()           /* Performance Hook (20/9C) */
    return(rc);
}

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

/***EP  VR32GiveSharedMem - attach a target process to existing memory object.
 *
 *      VR32GiveSharedMem is the Dos32GiveSharedMem validation routine.
 *      Arguments are validated and control is transfered to the worker
 *      routine.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              addr - base address of object to attach
 *              pid - id of process to attach
 *              flags - attribute flags
 *                  OBJ_FORK        inheritable on posix fork
 *                  OBJ_EXECUTE     inheritable on posix execute
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *      RETURN  0 on success, else error code
 */

int cdecl
VR32GiveSharedMem(sef, addr, pid, flags)
THUNKSEF sef;
ulong_t addr;
ulong_t pid;
ulong_t flags;
{

//  VMOI oi;
    VMOD od;
    register VMOB *pob;
    struct ptda_s *pptda;
    ulong_t rc;

    MMPHPreVM32ApiGiveSharedMem()               /* Performance Hook (20/1D) */

    /*
     * Validate flags.
     */
    if ((flags & fPERM) == 0 || (flags & ~(fGIVESHR|PAG_GUARD))) {
        return(ERROR_INVALID_PARAMETER);
    }

    /*
     * Address must be page aligned.
     */
    if ((addr & PAGEMASK) || addr >= MAXCRLADDR || addr < MINCRLADDR) {
        return(ERROR_INVALID_ADDRESS);
    }

    /*
     * Call worker.
     */
//    return(VM32ApiGiveSharedMem(addr, pid, flags));

    /*
     * Get the ptda semaphore so the target process won't slip
     * out from under us while we try to do the work.
     * TKPidGetSem also makes sure that the process isn't dying
     */
    if ((rc = (ulong_t) TKPidGetSem((ushort_t) pid, SSToDS(&pptda)))
        != NO_ERROR) {
        vmAssert(pTSDCur->TSDcObjSem == 0);
        MMPHPostVM32ApiGiveSharedMem()          /* Performance Hook (20/9D) */
        return(rc);
    }

    /*
     * Get the object information stucture and take its semaphore at the
     * same time.  Verify that the requested address is the object's base.
     */
//  if ((rc = VMGetMemInfo(VMGS_TAKESEM, addr, HOBNULL, SSToDS(&oi), 0)) !=
//      NO_ERROR) {

    od.od_laddr = addr;
    od.od_pPTDA = pPTDACur;
    if ((rc = VMGetOd(VMGS_TAKESEM, SSToDS(&od))) != NO_ERROR)
    {
        goto Error2;
    }

    pob = od.od_pob;

//  if (addr != oi.oi_va) {
    if (addr != od.od_laddr)
    {
        rc = ERROR_INVALID_ADDRESS;
        goto Error;
    }

    /*
     * Verify that the object is givable.
     */
//  if ((oi.oi_fsob & OB_SHARED) == 0 ||
//  (oi.oi_hobowner != GIVESEGOWNER && oi.oi_hobowner != SHRDOWNER)) {

    if ((pob->ob_fs & OB_SHARED) == 0 ||
         (pob->ob_hobowner != GIVESEGOWNER && pob->ob_hobowner != SHRDOWNER))
    {
        rc = ERROR_ACCESS_DENIED;
        goto Error;
    }

    /*
     * Translate flags.
     */
    flags = vmfTranslate(flags) + PG_U;

    /*
     * Do the attachment.
     */

//  rc = VMAttach(oi.oi_hob, pptda->ptda_handle, flags);
    rc = VMAttach(vmPobToHob(pob), pptda->ptda_handle, flags);

Error:
//  VMClearSem(oi.oi_hob);
    VMClearSem(vmPobToHob(pob));
Error2:
    VMClearSem(pptda->ptda_handle);
    vmAssert(pTSDCur->TSDcObjSem == 0);
    MMPHPostVM32ApiGiveSharedMem()              /* Performance Hook (20/9D) */
    return(rc);
}

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

/***EP  VR32FreeMem - Free a memory object.
 *
 *      VR32FreeMem is the Dos32FreeMem validation routine.  Arguments
 *      are validated and control is transfered to the worker routine.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              addr - base address of object to free
 *      RETURN  0 on success, else error code
 */

int cdecl
VR32FreeMem(sef, addr)
THUNKSEF sef;
ulong_t addr;
{

    VMOD                od;             /* return info struct */
    ulong_t             rc;

    MMPHPreVM32ApiFreeMem()                     /* Performance Hook (20/1E) */

    /*
     * Address must be page aligned.
     */
    if ((addr & PAGEMASK) || addr >= MAXCRLADDR || addr < MINCRLADDR) {
        return(ERROR_INVALID_ADDRESS);
    }

//    return(VM32ApiFreeMem(addr));

    /*
     * Get the object's virtual address and size, and make sure the
     * address passed in is the object's starting address.
     */
//  if ((rc = VMGetMemInfo(VMGS_TAKESEM, addr, HOBNULL, SSToDS(&oi), 0))
//      != NO_ERROR) {

    od.od_laddr = addr;
    od.od_pPTDA = pPTDACur;
    if ((rc = VMGetOd(VMGS_TAKESEM, SSToDS(&od))) != NO_ERROR)
    {
        vmAssert(pTSDCur->TSDcObjSem == 0);
        MMPHPostVM32ApiFreeMem()                /* Performance Hook (20/9E) */
        return(ERROR_INVALID_ADDRESS);
    }

//  if (addr != oi.oi_va) {

    if (addr != od.od_laddr)
    {
//      VMClearSem(oi.oi_hob);
        VMClearSem(vmPobToHob(od.od_pob));
        vmAssert(pTSDCur->TSDcObjSem == 0);
        MMPHPostVM32ApiFreeMem()                /* Performance Hook (20/9E) */
        return(ERROR_INVALID_ADDRESS);
    }

    /*
     * Don't allow the infosegs to be freed.
     */
    if (addr == SEL_TASKINFOLADDR || addr == SEL_SYSINFOLADDR)
    {
        rc = ERROR_ACCESS_DENIED;
    }

    /*
     * Check to see if the request is to free a resource, if it is
     * don't let them.  (Skip LDRIsResource if not shared.)
     */
//  vmAssert(oi.oi_hobptda == pPTDACur->ptda_handle);
    vmAssert(od.od_pPTDA == pPTDACur);

//  if (VMShrOi(oi) && vmIsLoadOb(vmHandleMap(oi.oi_hob)) &&
//    LDRIsResource(oi.oi_hob, oi.oi_hobmte, pPTDACur->ptda_rsrclist) ==
//    NO_ERROR)

    if (VMShrOd(od) && vmIsLoadOb(od.od_pob) &&
           LDRIsResource(vmPobToHob(od.od_pob), (od.od_pob)->ob_hobmte,
                             pPTDACur->ptda_rsrclist) == NO_ERROR)
    {
        rc = ERROR_ACCESS_DENIED;
    }

    /*
     * Handle all others.
     */
//  VMClearSem(oi.oi_hob);
    VMClearSem(vmPobToHob(od.od_pob));

    if (rc == NO_ERROR)
    {
        rc = VMFreeMem(addr, HOBNULL, VMFM_NOLDR);
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);

    MMPHPostVM32ApiFreeMem()                    /* Performance Hook (20/9E) */
    return(rc);
}

/***LP  vmScanContext - Call worker function for each context     153480 Start
 *
 *      VMScanContext walks the AR chain linked to the specified object record
 *      and calls the given worker if needed.  The displacement value is
 *      converted to a linear page number for each mapping.  This code does
 *      not block (and must not block).
 *
 *      NOTE:  - The handle semaphore is NOT taken.  This code assumes that the
 *               appropriate VM data structures (ob, ar, co) are properly linked
 *               together at all times.  This is done to avoid a semaphore
 *               hierarchy violation.
 *             - The worker function must not block.
 *             - There is no provision for the worker to return error.
 *
 *      ENTRY   pfworker    - pointer to worker function
 *              pdata       - pointer to data passed to worker
 *              dpg         - displacement from start of object
 *              hob         - handle of object record
 *      RETURN  none
 *
 */

typedef void (*pfnScanMaps)(void *pdata, page_t ipg, struct ptda_s *pptda);

int                     VMScanContext(pfworker, pdata, dpg, hob)
pfnScanMaps             pfworker;
void                    *pdata;
ushort_t                dpg;
VMHOB                   hob;
{
    VMHAR               har;            /* handle to arena record */
    register VMAR       *par;           /* pointer to arena record */
    VMHCO               hco;            /* handle to Context Record */
    VMOB                *pob;           /* pointer to object record */
    register VMCO       *pco;           /* pointer to Context Record */
    ulong_t             ipg;            /* page address passed to worker */
    ulong_t             rc = 0;

    TKBeginNoBlock();
    vmAssert(VMHandleVerify(hob,0,NULL) == NO_ERROR);

    pob = vmHandleMap(hob);             /* get pointer to object record */
    har = pob->ob_har;                  /* get first arena record handle */
    vmAssert(har != HARNULL);

    par = vmArMap(har);
    vmAssert(dpg < par->ar_cpg || vmAliasAr(par));
    ipg = par->ar_ipg + dpg;

    if (par->ar_f & AR_HCO) {

        hco = VMHcoAr(*par);
        vmAssert(hco != HCONULL);

        /*
         * Scan all context records linked to the arena record,
         * and call the worker on each one.
         */
        do {
            pco = vmCoMap(hco);
            hco = VMHconextCo(*pco);
            (*pfworker)(pdata, ipg, VMGetPTDAAddr(pco->co_hobptda));

        } while (hco != HCONULL);

    } else {     /* Freeing of private data is done in VMFreeMem */
        rc = ERROR_INVALID_ADDRESS;
    }

    TKEndNoBlock();
    return(rc);
}

/***LP  vmFreeMemContext - Free a memory object from a single user context
 *
 *      The is the worker for VMFreeAllContext routine for freeing
 *      a memory object from a single context.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   pod   - pointer to object data structure
 *              page  - virtual page number to free (laddr >> PAGESHIFT)
 *              pptda - PTDA context pointer
 *      RETURN  0 on success, else error code
 */
void
vmFreeMemContext(pod, page, pptda)
VMOD          *pod;
page_t         page;
struct ptda_s *pptda;
{
    pod->od_laddr = (page << PAGESHIFT);
    pod->od_pPTDA = pptda;
    TKEndNoBlock();
    VMFreeMemOd( (VMFM_NOLDR), pod );
    TKBeginNoBlock();
}

/***LP  VMFreeAllContext - Free a memory object from all user context
 *
 *      The is the Dos32FreeMem temporary replacement for freeing
 *      a memory object from all processes.  Arguments are
 *      validated and control is transfered to the worker routine.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   laddr - base address of object to free
 *      RETURN  0 on success, else error code
 */

int cdecl
VMFreeAllContext(laddr)
ulong_t laddr;
{

    VMOD                od;             /* return info struct */
    ulong_t             rc;

//  MMPHPreVM32ApiFreeMem()

    /*
     * Address must be page aligned.
     */
    if ((laddr & PAGEMASK) || (laddr >= MAXCRLADDR) || (laddr < MINCRLADDR)) {
        return(ERROR_INVALID_ADDRESS);
    }

    /*
     * Get the object's virtual address and size, and make sure the
     * address passed in is the object's starting address.
     */

    od.od_laddr = laddr;
    od.od_pPTDA = pPTDACur;
    if ((rc = VMGetOd(VMGS_TAKESEM, SSToDS(&od))) != NO_ERROR)
    {
        vmAssert(pTSDCur->TSDcObjSem == 0);
//      MMPHPostVM32ApiFreeMem()
        return(ERROR_INVALID_ADDRESS);
    }

    if (laddr != od.od_laddr)
    {
        VMClearSem(vmPobToHob(od.od_pob));
        vmAssert(pTSDCur->TSDcObjSem == 0);
//      MMPHPostVM32ApiFreeMem()
        return(ERROR_INVALID_ADDRESS);
    }

    /*
     * Don't allow the infosegs to be freed.
     */
    if (laddr == SEL_TASKINFOLADDR || laddr == SEL_SYSINFOLADDR)
    {
        rc = ERROR_ACCESS_DENIED;
    }

    /*
     * Check to see if the request is to free a resource, if it is
     * don't let them.  (Skip LDRIsResource if not shared.)
     */
    vmAssert(od.od_pPTDA == pPTDACur);

    if (VMShrOd(od) && vmIsLoadOb(od.od_pob) &&
           LDRIsResource(vmPobToHob(od.od_pob), (od.od_pob)->ob_hobmte,
                             pPTDACur->ptda_rsrclist) == NO_ERROR)
    {
        rc = ERROR_ACCESS_DENIED;
    }

    /*
     * Handle all others.
     */
    VMClearSem(vmPobToHob(od.od_pob));

    /*
     * Getting the pvp->vp_obpg may require the following:
     * 1. Get the page frame by doing a VMLinToPhys then >> PAGESHIFT
     * 2. Take the result from (1) and call pgFrameToPF to get a pointer
     *    to PF (ppf).
     * 3. Pointer to VP (pvp) is in ppf->pf_pvp
     */
    if (rc == NO_ERROR)
    {
        rc = VMScanContext( &vmFreeMemContext,        /* freemem worker      */
                            SSToDS(&od),              /* Object data         */
                            (ushort_t) 0,             /* Object page #       */
                            vmPobToHob(od.od_pob) );  /* Object handle       */
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);

//  MMPHPostVM32ApiFreeMem()
    return(rc);
}                                                              /* 153480 End */

#pragma code_seg()

/***EP  VR32SetMem - Commit/decommit memory, and change access permissions.
 *
 *      VR32SetMem is the Dos32SetMem validation routine.  Arguments
 *      are validated and control is transfered to the worker routine.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              addr - base address of range of pages to change
 *              size - size of region to change
 *              flags - attribute flags
 *                  PAG_COMMIT      commit all pages
 *                  PAG_DECOMMIT    decommit all pages
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *                  PAG_DEFAULT     provide default access permissions
 *      RETURN  0 on success, else error code
 */

int cdecl
VR32SetMem(sef, addr, size, flags)
THUNKSEF sef;
ulong_t addr;
ulong_t size;
ulong_t flags;
{

    VMOD od;
    int rc;

    MMPHPreVM32ApiSetMem()                      /* Performance Hook (20/1F) */

    /*
     * Address must be within compatibility region.
     */
    if (addr >= MAXCRLADDR || addr < MINCRLADDR) {
        return(ERROR_INVALID_ADDRESS);
    }

    /*
     * Validate flags and size.  If PAG_DECOMMIT is set, all other flags must
     * be off.
     */
    if ((size > VMMAXSIZE) ||
        ((addr+size) < addr) ||     //p46738-Check for overflow
        (flags != PAG_DECOMMIT &&
        ((flags & (fPERM|PAG_DEFAULT)) == 0 ||
        (flags & ((~(fSET|PAG_GUARD))|PAG_DECOMMIT)) ||
        ((flags & (fPERM|PAG_GUARD)) && (flags & PAG_DEFAULT))))) {
        return(ERROR_INVALID_PARAMETER);
    }

    /*
     * Call worker.
     */
//    return(VM32ApiSetMem(addr, size, flags));

    /*
     * Translate flags.
     */
    flags = ((flags & PAG_DEFAULT) ? VMS_DFLTPERM : 0) +
            ((flags & PAG_COMMIT) ? VMS_COMMIT|PG_SWAPPABLE|PG_ZEROFILL : 0) +
            vmfTranslate(flags);

    /*
     * Don't allow shared memory to be decommitted.
     */
    if (flags & VMS_DECOMMIT)
    {
//      if ((rc = VMGetMemInfo(0, addr, pPTDACur->ptda_handle, SSToDS(&oi),
//          0)) != NO_ERROR) {

        od.od_laddr = addr;
        od.od_pPTDA = pPTDACur;
        if ((rc = VMGetOd(NA, SSToDS(&od))) != NO_ERROR)
        {
            vmAssert(pTSDCur->TSDcObjSem == 0);
            MMPHPostVM32ApiSetMem()             /* Performance Hook (20/9F) */
            return(rc);
        }

// D# 52259        if (VMShrOi(oi)) {
//      if (VMShrOi(oi) && ((oi.oi_fsob & OB_SHRINKABLE)==0) )

        if (VMShrOd(od) && (((od.od_pob)->ob_fs & OB_SHRINKABLE) ==0))
        {
            MMPHPostVM32ApiSetMem()             /* Performance Hook (20/9F) */
            return(ERROR_INVALID_PARAMETER);
        }
    } else {
        flags |= PG_U;
    }

    if (size == 0)
    {
        MMPHPostVM32ApiSetMem()                 /* Performance Hook (20/9F) */
        return(ERROR_INVALID_PARAMETER);
    }

    if (addr == SEL_TASKINFOLADDR || addr == SEL_SYSINFOLADDR)
    {
        MMPHPostVM32ApiSetMem()                 /* Performance Hook (20/9F) */
        return(ERROR_ACCESS_DENIED);
    }

    rc = VMSetMem(addr, size, HOBNULL, flags, NA);

    vmAssert(pTSDCur->TSDcObjSem == 0);
    MMPHPostVM32ApiSetMem()                     /* Performance Hook (20/9F) */
    return(rc);
}

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

/***EP  VR32QueryMem - Get page information.
 *
 *      VR32QueryMem is the Dos32QueryMem validation routine.  Arguments
 *      are validated and control is transfered to the worker routine.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              addr - base address of pages to be queried
 *              psize - pointer to location in user space that contains the
 *                      requested size of region to query.
 *              pflags - pointer to location in user space that will receive
 *                       the attribute flags describing the region
 *      RETURN  0 on success,
 *                *psize - contains actual size of region queried
 *                *pflags - attibute flags describing region
 *                  PAG_COMMIT      pages are committed
 *                  PAG_RESERVE     pages are reserved
 *                  PAG_FREE        pages are free
 *                  PAG_PRIVATE     pages are in private object
 *                  PAG_SHARED      pages are in shared object
 *                  PAG_BASE        addr is base of object
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *              else error code
 */

int cdecl
VR32QueryMem(sef, addr, psize, pflags)
THUNKSEF sef;
ulong_t addr;
ulong_t *psize;
ulong_t *pflags;
{
    ulong_t sizeL;
    ulong_t flagsL;
    ulong_t rc;

    /* PTR: B725695
     * Used to hold the original size passed in by the user.
     * After this routine finishes we pass information back to
     * the user with TKSuDWordNF function. But after we change
     * the size, there is a possibility that we can fault while
     * setting the flags (if the pointer has been invalidated).
     * In this case we must change the size back to it's original
     * value.
     */
    ulong_t OriginalSize;

    /* PTR: B725695
     * Validate psize, pflags before they're used (MMIOPH kernel only).
     * Since these addresses may be used by the hook and since
     * hooks execute at the CPL (current protection level)
     * we could trap inside the kernel.
     */
    #ifdef  MMIOPH
       if(TKProbeBuff((char *)psize,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
       if(TKProbeBuff((char *)pflags,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
    #endif

    /*
     * Address must be within compatibility region.
     */
    if (addr >= MAXCRLADDR || addr < MINCRLADDR) {
        return(ERROR_INVALID_ADDRESS);
    }

    /*
     * Fetch size to query.
     */
    if (TKFuDWordNF(SSToDS(&sizeL), psize)) {     /* PTR: B725695 */
        return(ERROR_INVALID_PARAMETER);
    }

    OriginalSize=sizeL;                           /* PTR: B725695 */

    /*
     * Cap the size.
     */
    if (sizeL > VMMAXSIZE) {
        sizeL = VMMAXSIZE;
    }

    /*
     * Call worker and pass size and flags back to user if no error.
     */
    if ((rc = VM32ApiQueryMem(addr, SSToDS(&sizeL), SSToDS(&flagsL))) == 0 ) {
        /* PTR: B725695
         * Use TKSuDWordNF since psize or pflags
         * may have been invalidated.
         */
        if(TKSuDWordNF(sizeL, psize)) goto BadParm1;
        if(TKSuDWordNF(flagsL, pflags)) {
           vmAssert(TKSuDWordNF(OriginalSize, psize) == NO_ERROR);
BadParm1:
           return(ERROR_INVALID_PARAMETER);
        }
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    return(rc);
}

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

/***EP  VR32QueryMemState - Get page state information.
 *
 *      VR32QueryMemState is the Dos32QueryMemState validation routine.
 *      Arguments are validated and control is transfered to the worker
 *      routine.  Note that this is a temporary API--DosQueryProcStatus
 *      will replace this.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sef - system entry frame
 *              addr - base address of pages to be queried
 *              psize - pointer to location in user space that contains the
 *                      requested size of region to query.
 *              pflags - pointer to location in user space that will receive
 *                       the attribute flags describing the region
 *      RETURN  0 on success,
 *                *psize - contains actual size of region queried
 *                *pflags - state flags describing region
 *                 one of:
 *                  PAG_PRESENT     pages are present
 *                  PAG_NPIN        pages are not present, and in core
 *                  PAG_NPOUT       pages are not present, and not in core
 *                 one of:
 *                  PAG_SWAPPABLE   pages are swappable
 *                  PAG_DISCARDABLE pages are discardable
 *                  PAG_RESIDENT    pages are resident
 *                  PAG_INVALID     pages are either free or not committed
 *              else error code
 */

int cdecl
VR32QueryMemState(sef, addr, psize, pflags)
THUNKSEF sef;
ulong_t addr;
ulong_t *psize;
ulong_t *pflags;
{
    ulong_t sizeL;
    ulong_t flagsL;
    ulong_t rc;

    /* PTR: B725695
     * Used to hold the original size passed in by the user.
     * After this routine finishes we pass information back to
     * the user with TKSuDWordNF function. But after we change
     * the size, there is a possibility that we can fault while
     * setting the flags (if the pointer has been invalidated).
     * In this case we must change the size back to it's original
     * value.
     */
    ulong_t OriginalSize;

    /* PTR: B725695
     * Validate psize, pflags before they're used (MMIOPH kernel only).
     * Since these addresses may be used by the hook and since
     * hooks execute at the CPL (current protection level)
     * we could trap inside the kernel.
     */
    #ifdef  MMIOPH
       if(TKProbeBuff((char *)psize,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
       if(TKProbeBuff((char *)pflags,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
    #endif

    /*
     * Address must be within compatibility region.
     */
#ifdef OMIT
    if (addr >= MAXCRLADDR || addr < MINCRLADDR) {
        return(ERROR_INVALID_ADDRESS);
    }
#endif

    /*
     * Fetch size to query.
     */
    if (TKFuDWordNF(SSToDS(&sizeL), psize)) {     /* PTR: B725695 */
        return(ERROR_INVALID_PARAMETER);
    }

    OriginalSize=sizeL;                           /* PTR: B725695 */

    /*
     * Cap the size.
     */
    if (sizeL > VMMAXSIZE) {
        sizeL = VMMAXSIZE;
    }

    /*
     * Call worker and pass size and flags back to user if no error.
     */
    if((rc=VM32ApiQueryMemState(addr, SSToDS(&sizeL), SSToDS(&flagsL))) == 0) {
        /* PTR: B725695
         * Use TKSuDWordNF since psize or pflags
         * may have been invalidated.
         */
        if(TKSuDWordNF(sizeL, psize)) goto BadParm2;
        if(TKSuDWordNF(flagsL, pflags)) {
           vmAssert(TKSuDWordNF(OriginalSize, psize) == NO_ERROR);
BadParm2:
           return(ERROR_INVALID_PARAMETER);
        }
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    return(rc);
}




/***    VR32QueryPageUsage - Dos32QueryPageUsage validation routine
 *
 *      PROFILE
 *      Make sure that the current caller is the valid caller.  Then,
 *      parameters are validated as best as possible and the appropriate
 *      worker routine is called.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ulong_t VR32QueryPageUsage(THUNKSEF sef, ushort_t usSubFunc,
 *                                 PVOID pvInfoBuffer, ulong_t *pcbBuf)
 *
 *      ENTRY:  sef - system entry frame
 *              usSubFunc - desired subfunction to perform
 *                  QPU_REGAPP = register caller as monitoring app(s)'s
 *                               compatibility region (CR). If monitoring
 *                               a single app, find its PTDA
 *                  QPU_GETAPP = get list of pages, access bits, and sizes in
 *                               the CR
 *                  QPU_REGSYS = register caller as monitoring system arena
 *                  QPU_GETSYS = get list of pages, access bits, and sizes in
 *                               system arena
 *                  QPU_GETGSR = get list of pages, access bits, and sizes in
 *                               Global Shared Region
 *                  QPU_DEREG  = stop monitoring both system arena and app(s)'s  *                               CR, deregister caller, and re-enable aging.
 *              pvInfoBuffer -
 *                  usSubFunc = QPU_REGAPP, ptr to app name.
 *                              If NULL, monitor all apps.
 *                  usSubFunc = QPU_GETAPP or QPU_GETSYS or QPU_GETSHR,
 *                              ptr to page info structs
 *              pcbBuf - ptr to size, in bytes, of pvInfoBuffer
 *                  when usSubFunc = QPU_GETAPP or QPU_GETSYS or QPU_GETSHR
 *                  and used to return number of structs filled
 *
 *      EXIT:   0 - no error
 *              ERROR_NOT_ENOUGH_MEMORY
 *              ERROR_INVALID_PROCID
 *              ERROR_BUFFER_OVERFLOW
 *              ERROR_INVALID_LEVEL
 *              Or whatever is returned by the other generic functions called
 *
 */

ulong_t cdecl
VR32QueryPageUsage(sef, usSubFunc, pvInfoBuffer, pcbBuf)
THUNKSEF sef ;
ushort_t usSubFunc ;
PVOID pvInfoBuffer ;
ulong_t *pcbBuf ;
{

    struct ptda_s *pptda ;          // Ptr to owner's or monitored app's PTDA
    char *szapp ;                   // Resident copy of name of app to monitor
    ulong_t bufsize, cpysize ;      // # of bytes needed for buffer
    PQPUINFO pCpyResInfBuf ;        // modified by qpuGetPageInfo
    qpuArgs_t qpuArgs ;             // Used to pass args to TKScanTasks worker
    PTCB pTCB ;                     // pointer to a TCB
    long oldcount ;
    ulong_t rc = NO_ERROR ;         // Return code

    /* PTR: B725695
     * Used to hold the original size passed in by the user.
     * After this routine finishes we pass information back to
     * the user with TKSu??NF functions. But after we change
     * the size, there is a possibility that we can fault while
     * setting the page info strucs (if the pointer has been invalidated).
     * In this case we must change the size back to it's original
     * value.
     */
    ulong_t OriginalPcbBuf;

    /* PTR: B725695
     * Validate pcbBuf, pvInfoBuffer before they're used (MMIOPH kernel only).
     * Since these addresses may be used by the hook and since
     * hooks execute at the CPL (current protection level)
     * we could trap inside the kernel.
     */
    #ifdef  MMIOPH
       int DummyLen;  /* Used to validate pvInfoBuffer */
       if(TKProbeBuff((char *)pcbBuf,sizeof(ulong_t *),
          (TK_UM_READ|TK_UM_WRITE)))
             return(ERROR_INVALID_PARAMETER);
       if(pvInfoBuffer)
          if(TKFuBuffLenz(SSToDS(&DummyLen),(char *)pvInfoBuffer,256,0))
             return(ERROR_INVALID_PARAMETER);
    #endif

    if (ApuOwner.pptda != NULL) {

        // If Dos32QueryPageUsage has been called before, the previous caller
        // is still alive (and thus considered the only valid caller), and the
        // current caller is NOT the previous caller, return ERROR_ACCESS_DENIED
        // This ensures exclusive access to the API.

        pptda = ApuOwner.pptda ;
        if ((TKiSlotTo_pTCB(ApuOwner.slot,SSToDS(&pTCB)) != NO_ERROR)
         || (pTCB->TCBpPTDA != pptda)
         || (pptda->ptda_Pid == ApuOwner.pid
             && pPTDACur->ptda_Pid != ApuOwner.pid))

                return(ERROR_ACCESS_DENIED) ;
    }

    // Use usSubFunc to determine the parameter validation to perform and the
    // worker to call.

    switch (usSubFunc) {
        case QPU_REGAPP:    // Register caller as app or system monitor
        case QPU_REGSYS:

            if (!fRegistered) {
                fRegistered = TRUE ;

                // Establish valid caller
                ApuOwner.pptda = pPTDACur ;
                ApuOwner.pid = pPTDACur->ptda_Pid ;
                ApuOwner.slot = pPTDACur->ptda_pTCBHead->TCBNumber ;

                // Call the ager to clear out all unused pages so that
                // the system starts out 'clean' to avoid system lockup.
                // This must be done before the ager is turned off (by
                // setting either MonSys.pptda or MonApp.pptda).
                // pgListCount is the list of idle+free pages in the system.
                // Every time the ager runs, this count should increase
                // until we are out of ageable memory.

                do {
                    oldcount = pgListCount;
                    pgAgeRate(&pgAgeRateTable[0]);
                    pgIdleList.li_flags |= LI_BLOCKED;
                    if ( (rc = TKBlock(&pgIdleList, -1, 0)) != NO_ERROR)
                        return(rc);
                } while (oldcount != pgListCount);
            }

            if (usSubFunc == QPU_REGSYS) {
                MonSys.pptda = pPTDACur ;
                MonSys.pid = pPTDACur->ptda_Pid ;
                MonSys.slot = pPTDACur->ptda_pTCBHead->TCBNumber ;
                return(NO_ERROR) ;
            }

            // If the app name string is NULL, set up to monitor all apps.

            if (pvInfoBuffer == NULL) {               /* PTR: B725695 */
                MonApp.pptda = QPU_ALLAPPS;
                MonApp.pid = MonApp.slot = 0 ;
                return(NO_ERROR);
            }

            // Otherwise, find the app.

            if ((rc = VMAllocKHB(VM_HKH_PUB_RESRW, 256, PUSAGETMPOWNER,
                NA, SSToDS(&szapp))) == 0) {
                if(TKFuBuffz(NULL, szapp, pvInfoBuffer, 256, 0)) { /* PTR: B725695 */
                   VMFreeKHB(VM_HKH_PUB_RESRW, szapp) ;
                   return(ERROR_INVALID_PARAMETER);
                }
                rc = qpuFindApp(szapp) ;
                VMFreeKHB(VM_HKH_PUB_RESRW, szapp) ;
            } ;

            if (rc != NO_ERROR)
                ApuOwner.pptda = MonApp.pptda = NULL ;

            return(rc) ;

        case QPU_GETAPP:    // Get page usage info on app or system
        case QPU_GETSYS:
        case QPU_GETGSR:

            // Ensure application has registered
            if (!fRegistered)
                return(ERROR_INVALID_LEVEL);

            // Make sure we have a resident PGINFO buffer to use of the right
            // size.
            if(TKFuDWordNF(SSToDS(&bufsize), pcbBuf)) { /* PTR: B725695 */
              return(ERROR_INVALID_PARAMETER);
            }
            OriginalPcbBuf=bufsize;                     /* PTR: B725695 */
            cpysize = bufsize;          // save starting size of buffer
            if (pResInfBuf == NULL)
                rc = VMAllocKHB(VM_HKH_PUB_RESRW, bufsize, PUSAGEOWNER,
                    NA, &pResInfBuf) ;
            else if (cbRIB < bufsize)
                rc = VMReallocKHB(VM_HKH_PUB_RESRW, &pResInfBuf, bufsize, 0) ;
            if (rc != 0) {
                pResInfBuf = NULL ;
                cbRIB = 0 ;
                return(rc) ;
            } ;
            cbRIB = (cbRIB >= bufsize) ? cbRIB : bufsize ;
            pCpyResInfBuf = pResInfBuf ;

            // Ensure that the ager has not had to restart to avoid
            // system lockup.

            pptda = (usSubFunc == QPU_GETAPP) ? MonApp.pptda : MonSys.pptda ;
            if (pptda == NULL)
                return(ERROR_NOT_ENOUGH_MEMORY);

            // Call qpuGetPageInfo() to get info from the appropriate arena

            // Set up args for qpuGetPageInfo. Args are passed this way
            // so that qpuGetPageInfo can function as a worker for TKScanTasks

            qpuArgs.ppbuf = SSToDS(&pCpyResInfBuf);
            qpuArgs.pbufsize = SSToDS(&bufsize);

            if (usSubFunc == QPU_GETSYS) {
                pptda = MonSys.pptda ;
                qpuArgs.pgstart = MINSYSPAGE;
                rc = qpuGetPageInfo(pptda, SSToDS(&qpuArgs)) ;
            }
            else {
                pptda = MonApp.pptda ;
                qpuArgs.pgstart = MINCR_PAGE;

            // KDH - Set up for Global Shared Region

                if (usSubFunc == QPU_GETGSR) {
                    qpuArgs.pgstart = MINGLOBALSHRPAGE;
                }

                if (pptda == QPU_ALLAPPS) {

                    rc = TKScanTasks(TK_TG_ALL|TK_SF_WORKER|TK_SF_WRETCODE,
                                        0, qpuGetPageInfo,
                                        (void *) SSToDS(&qpuArgs)) ;
                }
                else {
                    // Make sure that the app being monitored is still alive

                    if ( (TKiSlotTo_pTCB(MonApp.slot,SSToDS(&pTCB)) != NO_ERROR)
                         || ((pptda = pTCB->TCBpPTDA) != MonApp.pptda)
                         || ( pptda->ptda_Pid != MonApp.pid) ) {

                        MonApp.pptda = NULL ;
                        MonApp.slot = 0 ;
                        return(ERROR_INVALID_LEVEL) ;
                    }

                    // Get the target PTDA semaphore

                    rc = VMGetSem(pptda->ptda_handle,
                                  VMGS_TAKESEM | VMGS_IGNINT | VMGS_PSEUDO,
                                  SSToDS(&pptda));

                    // Make sure that the ager has not restarted and cleared
                    // MonApp.pptda; and that the app being monitored is still
                    // alive again - we could have blocked between the first
                    // check and grabbing the semaphore.

                    if (MonApp.pptda == NULL)
                        return(ERROR_NOT_ENOUGH_MEMORY) ;
                    if ( (TKiSlotTo_pTCB(MonApp.slot,SSToDS(&pTCB)) != NO_ERROR)
                         || ((pptda = pTCB->TCBpPTDA) != MonApp.pptda)
                         || ( pptda->ptda_Pid != MonApp.pid) ) {

                        MonApp.pptda = NULL ;
                        return(ERROR_INVALID_LEVEL) ;
                    }

                    rc = qpuGetPageInfo(pptda, SSToDS(&qpuArgs)) ;

                    // Release the target PTDA semaphore

                    VMClearSem(pptda->ptda_handle);
                } ;
            } ;

            // Copy info back into user space
            // bufsize has size of unused buffer. Use to compute size to
            // write out to user buffer

            if (rc == NO_ERROR) {
                cpysize -= bufsize ;
                /* PTR: B725695
                 * Use TKSu??NF since pcbBuf, pvInfoBuffer
                 * may have been invalidated.
                 */
                if (TKSuDWordNF(cpysize, pcbBuf)) goto BadParm3;
                if (TKSuBuff(pvInfoBuffer, (char *) pResInfBuf,cpysize,0)) {
                     vmAssert(TKSuDWordNF(OriginalPcbBuf, pcbBuf) == NO_ERROR);
                     VMFreeKHB(VM_HKH_PUB_RESRW, pResInfBuf);
                     pResInfBuf = NULL ;
                     cbRIB = 0 ;
BadParm3:
                     return(ERROR_INVALID_PARAMETER);
                }
            } ;
            return(rc) ;

        case QPU_DEREG:         // Deregister app/system monitoring app
            fRegistered = FALSE ;
            ApuOwner.pptda = NULL ;
            ApuOwner.pid = ApuOwner.slot = 0 ;

             // Clearing these fields re-enables page aging.
            MonSys.pptda = MonApp.pptda = NULL ;

            // Free pResInfBuf

            if (pResInfBuf != NULL) {
                VMFreeKHB(VM_HKH_PUB_RESRW, pResInfBuf) ;
                pResInfBuf = NULL ;
                cbRIB = 0 ;
            } ;

            return(NO_ERROR) ;

        default:
            return(ERROR_INVALID_LEVEL) ;
    } ;
}


/***EP  VM32ApiAliasMem - Create an alias to a memory object.
 *
 *      VM32ApiAliasMem is the worker routine for the Dos32AliasMem api.
 *      This api creates an alias mapping a specified memory object.
 *      The object must exist in the current context, and can be in either
 *      the private or shared arena.  The access permissions granted to the
 *      alias are the same as the object being aliased, unless specified
 *      otherwise.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   addr - page aligned address in the object to alias
 *              size - size of region to alias
 *              palias - pointer to location in user space to receive alias
 *                       address
 *              flags - flags
 *      RETURN  0 on success, else error code
 *                *palias - alias's base address
 */

ulong_t
VM32ApiAliasMem(addr, size, palias, flags)
ulong_t addr;
ulong_t size;
ulong_t *palias;
ulong_t flags;
{
    VMAC ac;
    ulong_t flsel;
    ulong_t flvm =VMAC_ARENAPRV | VMAC_SELALLOC | VMAC_LOCANY;
    ulong_t rc;
    VMOD    od;
    ulong_t cbdesc;

    MMPHPreVM32ApiAliasMem()                    /* Performance Hook (20/18) */

    /* Make sure disallowed flags are not specified */

    if (flags & ~(OBJ_TILE | OBJ_SELMAPALL | SEL_CODE | SEL_USE32))
      {
        MMPHPostVM32ApiAliasMem()               /* Performance Hook (20/98) */
        return(ERROR_INVALID_PARAMETER);
      }

    /* Convert these to SELMGR flags */

    flsel =  vmAPIF2[(flags & (SEL_CODE | SEL_USE32))];

    /*
     * Set up alias permissions. If OBJ_SELMAPALL is not specified, the
     * alias will have the same permissions as the object
     */
    if (flags & OBJ_SELMAPALL)
      {
        flsel |= SELAL_BASEALL;
        flvm  |= PG_U | PG_W | PG_R;

        /*
         * If OBJ_SELMAPALL is specified, make sure laddr+size does not
         * exceed the object's committed size.
         */

        VMPTDAOd(od) = pPTDACur;
        VMLaddrOd(od) = addr;
        if ((rc = VMGetOd(NA, SSToDS(&od))) != NO_ERROR)
            MMPHPostVM32ApiAliasMem()           /* Performance Hook (20/98) */
            return(ERROR_INVALID_PARAMETER);
        rc = SELGetSize(VMLaddrOd(od),
                        NULL,
                        HOBNULL, VMSizeOd(od), SSToDS(&cbdesc));
        vmAssert(rc == NO_ERROR);
        if ((VMLaddrOd(od) + cbdesc) < (addr + size))
            MMPHPostVM32ApiAliasMem()           /* Performance Hook (20/98) */
            return(ERROR_INVALID_PARAMETER);

      }
    else
      {

        /*
         * Round the size up to multiple of page size.
         */

        size = (size + PAGEMASK) & ~PAGEMASK;
        /*
         * Size must be > 0 and < VMMAXSIZE.
         */

        if (size == 0 || size > VMMAXSIZE)
          {
            MMPHPostVM32ApiAliasMem()           /* Performance Hook (20/98) */
            return(ERROR_INVALID_PARAMETER);
          }

      };

    /*
     * Call VM to create an alias.
     */

    if ((rc = VMMapAlias(flvm,
                         flsel,
                         0,
                         0,
                         HOBNULL,
                         addr,
                         HOBNULL,
                         size,
                         SSToDS(&ac))) == NO_ERROR) {
        *palias = (ulong_t)ac.ac_va;
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    MMPHPostVM32ApiAliasMem()                   /* Performance Hook (20/98) */
    return(rc);
}

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

/***EP  VM32ApiAllocSharedMem - Allocate a memory object in the shared arena.
 *
 *      VM32ApiAllocSharedMem is the worker routine for the Dos32AllocSharedMem
 *      api.  This api allocates an object of the specified size in the shared
 *      arena.  If the request is for a tiled object, as many selectors as
 *      required to map the entire object are allocated as well.  An optional
 *      name string to associate with the memory object can be specified, which
 *      can be used by other tasks in the Dos32GetNamedSharedMem api.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   paddr - pointer to location in user space to store object addr
 *              pname - optional pointer to name string to associate with the
 *                      object.
 *              size - size of object to allocate
 *              flags - allocation flags
 *                  OBJ_TILE        allocate in tiled arena
 *                  OBJ_PROTECTED   allocate protected memory
 *                  OBJ_GIVEABLE    giveable to other tasks
 *                  OBJ_GETTABLE    gettable from other tasks
 *                  OBJ_FORK        inheritable on posix fork
 *                  OBJ_EXECUTE     inheritable on posix execute
 *                  PAG_COMMIT      commit all pages
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *              psef - pointer to system entry frame
 *      RETURN  0 on success,
 *                *paddr - object's base address
 *              else error code
 */

ulong_t
VM32ApiAllocSharedMem(paddr, pname, size, flags, psef)
ulong_t *paddr;
char *pname;
ulong_t size;
ulong_t flags;
THUNKSEF *psef;
{
    ushort_t selflags;
    ulong_t szCommit;
    ulong_t rc;
    VMAC ac;
    VMHOB owner;
    VMHOB creatingMTE;
    ulong_t fdecommitable;   // D# 52259

    MMPHPreVM32ApiAllocSharedMem()              /* Performance Hook (20/1A) */

    fdecommitable = flags;      // D# 52259
    flags &= ~PG_DECOMMITABLE;  // D# 52259

    /*
     * Size must be > 0.  Round the size up to multiple of page size.
     */
    size = (size + PAGEMASK) & ~PAGEMASK;
    if (size == 0) {
        MMPHPostVM32ApiAllocSharedMem()         /* Performance Hook (20/9A) */
        return(ERROR_INVALID_PARAMETER);
    }

    /*
     * Either entire object is committed, or none of it is committed.
     */
    szCommit = (flags & PAG_COMMIT) ? size : 0;

    /*
     * Set the owner.
     */
    if (rc = flags & (OBJ_GETTABLE|OBJ_GIVEABLE)) {
        if (rc == OBJ_GETTABLE) {
            owner = GETSEGOWNER;
        } else if (rc == OBJ_GIVEABLE) {
            owner = GIVESEGOWNER;
        } else {
            owner = SHRDOWNER;
        }

        /*
         * The get/give flags are not used below the layer of the worker.
         */
        flags &= ~(OBJ_GETTABLE|OBJ_GIVEABLE);
    } else {
        owner = HOBNULL;
    }

    /*
     * Figure out the caller's MTE (for ownership tracking)
     */
    creatingMTE = VMGetOwner(psef->thunksef_sef.sef_cs,
            psef->thunksef_sef.sef_eip);

    /*
     * Translate flags.
     */
    flags = vmfTranslate(flags);

    /*
     * Set additional VM and PG flags.  Force all allocations to be tiled.
     */
    flags |= VMAC_ALIGNSEL + VMAC_SELALLOC + VMAC_LOCANY + VMAC_ARENASHR +
             VMAC_OBAPI + PG_ZEROFILL + PG_SWAPPABLE + PG_U + VMAC_SHARED;

    /*
     * Set selector manager flags.
     */
    selflags = (ushort_t)(SELAL_SELMAP + (3 << SELAL_PRIVSHIFT) +
               ((flags & PG_W) ? SELAL_W : 0));

    /*
     * Set shrinkable bit if memory is decommitable
     */
    if (fdecommitable & PG_DECOMMITABLE)  {      // D# 52259
         flags |= VMAC_OBSHRINK;                 // D# 52259
    }
    /*
     * Use 16 bit worker to handle named shared memory.
     * The 16 bit code requires a 16:16 address to a kernel buffer for
     * the name string, so make sure to get a 16:16 address back from
     * GetTempBuffer.
     */
    if (pname) {
        char *pBufT;                    /* flat ptr to temp buffer */
        ulong_t pBufT16;                /* 16:16 ptr to temp buffer */

        rc = VMGetTempBuffer(SSToDS(&pBufT), SSToDS(&pBufT16));
        if (rc) {
            MMPHPostVM32ApiAllocSharedMem()     /* Performance Hook (20/9A) */
            return(rc);
        }
        if(TKFuBuffz(NULL, pBufT, pname, TEMPBUFLEN, 0)) {  /* PTR: B725695 */
           VMFreeTempBuffer(pBufT);
           MMPHPostVM32ApiAllocSharedMem()      /* Performance Hook (20/9A) */
           return(ERROR_INVALID_PARAMETER);
        }
        PathCharTrans(pBufT);

        rc = vmAllocNamedShrMem(size, szCommit, pBufT16, selflags, flags,
            SSToDS(&ac), creatingMTE);

        if (rc == NO_ERROR) {
            *paddr = (ulong_t)ac.ac_va;
        };
        VMFreeTempBuffer(pBufT);
        vmAssert(pTSDCur->TSDcObjSem == 0);
        MMPHPostVM32ApiAllocSharedMem()         /* Performance Hook (20/9A) */
        return(rc);
    }

    /*
     * Allocate some memory.
     */
    if ((rc = VMAllocMem(size,          /* size in bytes */
                         szCommit,      /* commit size */
                         flags,         /* allocation flags */
                         HOBNULL,       /* ptda handle */
                         owner,         /* owner */
                         creatingMTE,   /* mte handle */
                         selflags,      /* selector manager flags */
                         0,             /* loader block number */
                         SSToDS(&ac))) == NO_ERROR) { /* return buffer */
         *paddr = (ulong_t)ac.ac_va;
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    MMPHPostVM32ApiAllocSharedMem()             /* Performance Hook (20/9A) */
    return(rc);
}

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

/***EP  VM32ApiGetNamedSharedMem - Attach to existing named shared memory obj.
 *
 *      VM32ApiGetNamedSharedMem is the worker routine for the
 *      Dos32GetNamedSharedMem api.  This api attaches the requesting process
 *      to the specified shared memory object.  The object must have been
 *      allocated via Dos32AllocSharedMem with the name parameter and the
 *      OBJ_GETTABLE flag, or via Dos16AllocShrSeg.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   paddr - pointer to location in user space to receive object
 *                      address
 *              pname - pointer to name string associated with the object
 *              flags - attribute flags
 *                  OBJ_FORK        inheritable on posix fork
 *                  OBJ_EXECUTE     inheritable on posix execute
 *                  PAG_EXECUTE     provide execute access
 *                  PAG_READ        provide read access
 *                  PAG_WRITE       provide write access
 *                  PAG_GUARD       provide guard protection
 *      RETURN  0 on success,
 *                *paddr - object's base address
 *              else error code
 */

ulong_t
VM32ApiGetNamedSharedMem(paddr, pname, flags)
ulong_t *paddr;
char *pname;
ulong_t flags;
{
    int rc;
    char *pBufT;                        /* flat pointer to temp buffer */
    ulong_t pBufT16;                    /* 16:16 pointer to temp buffer */
    VMAC    ac;

    MMPHPreVM32ApiGetNamedShared()              /* Performance Hook (20/1B) */

    /*
     * Translate flags.
     */
    flags = vmfTranslate(flags) | PG_U;

    /*
     * Get the name string into a kernel buffer and translate any
     * unruly forward slashes.
     */
    rc = VMGetTempBuffer(SSToDS(&pBufT), SSToDS(&pBufT16));
    if (rc) {
        vmAssert(pTSDCur->TSDcObjSem == 0);
        MMPHPostVM32ApiGetNamedShared()         /* Performance Hook (20/9B) */
        return(rc);
    }
    if(TKFuBuffz(NULL, pBufT, pname, TEMPBUFLEN, 0)) {      /* PTR: B725695 */
        VMFreeTempBuffer(pBufT);
        vmAssert(pTSDCur->TSDcObjSem == 0);
        MMPHPostVM32ApiGetNamedShared()         /* Performance Hook (20/9B) */
        return(ERROR_INVALID_PARAMETER);
    }
    PathCharTrans(pBufT);

    /*
     * Call the 16 bit worker to get the memory.
     */
    rc = vmGetNamedShrMem(pBufT16, flags, SSToDS(&ac));

    if (rc == NO_ERROR) {
        *paddr = (ulong_t)ac.ac_va;
    };

    /*
     * Dump the temp buffer.
     */
    VMFreeTempBuffer(pBufT);
    vmAssert(pTSDCur->TSDcObjSem == 0);
    MMPHPostVM32ApiGetNamedShared()             /* Performance Hook (20/9B) */
    return(rc);
}

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

/***EP  VM32ApiQueryMem - Get page information.
 *
 *      VM32ApiQueryMem is the worker routine for the Dos32QueryMem api.
 *      This api queries the state of a range of memory.  The state and
 *      actual size queried are returned.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   addr - starting address of range to query.  This address is
 *                     byte granular and will NOT be rounded down to a page
 *                     boundary.
 *              psize - pointer to size in bytes of range to query (this value
 *                      will NOT be rounded up to page size multiple).  Will
 *                      receive the actual size in bytes of the range having
 *                      the state returned in flags.
 *              pflags - pointer to a variable to receive the state of the
 *                       memory queried.
 *      RETURN  0 on success, flags contains:
 *                      PAG_FREE        region is free
 *                      PAG_RESERVED    region is contained in an object
 *                      PAG_BASE        addr is the address of an object
 *                      PAG_PRIVATE     region is in the private arena
 *                      PAG_SHARED      region is in the shared arena
 *                      PAG_COMMIT      region is committed
 *                      PAG_EXECUTE     execute access is allowed
 *                      PAG_READ        read access is allowed
 *                      PAG_WRITE       write access is allowed
 *                      PAG_GUARD       guard protection is provided
 *              else error code
 */

ulong_t
VM32ApiQueryMem(addr, psize, pflags)
ulong_t addr;
ulong_t *psize;
ulong_t *pflags;
{
    ulong_t flagsL = VMQ_ACCESS;
    ulong_t sizeL = *psize;
    ulong_t rc;
    ulong_t fl;
    int cb;
//  VMOI oi;
    VMOD od;
    ulong_t flsem = VMGS_TAKESEM;
    struct seldesc_s desc;

#define PQ_PMASK (PQ_R|PQ_W|PQ_X|PQ_GUARD)

    MMPHPreVM32ApiQueryMem()                    /* Performance Hook (20/21) */

    if (*psize == 0) {
        MMPHPostVM32ApiQueryMem()               /* Performance Hook (20/A1) */
        return(ERROR_INVALID_PARAMETER);
    }

    /* Take the semaphore, since we may block. (in swappable code) */

//  if ((rc = VMGetMemInfo(VMGS_TAKESEM | VMGS_VERIFY, addr,
//      pPTDACur->ptda_handle,
//      SSToDS(&oi), 0)) != NO_ERROR) {

    od.od_laddr = addr;
    od.od_pPTDA = pPTDACur;
    if ((rc = VMGetOd(VMGS_TAKESEM | VMGS_VERIFY, SSToDS(&od))) != NO_ERROR)
    {
        if (rc == ERROR_INVALID_ADDRESS)
        {
            /* The region may belong to an invalid but prereserved section.
             * So go ahead and call VMQueryMem which will return the
             * expected flags values back. We don't need object semaphore
             * in this case
             */
            flsem = NULL;
        }
        else
        {
            MMPHPostVM32ApiQueryMem()           /* Performance Hook (20/A1) */
            return(rc);
        };
    };

    if ((rc = VMQueryMem(addr, psize, HOBNULL, SSToDS(&flagsL))) == NO_ERROR)
    {
        /*
         * VMQueryMem rounds addr and size to page granular values.  Compute
         * the size based on the original byte granular values.
         *
         * *psize was set (by VMQueryMem) to the page-granular number of
         * bytes of the enclosing page-granular region having a constant set
         * of attributes.  We must subtract from *psize the part of the first
         * page that does not belong to the byte-granular region and clamp the
         * value to the size of the region.
         */
        vmAssert(*psize > (addr & PAGEMASK));
        *psize -= addr & PAGEMASK;
        if (*psize > sizeL)
        {
            *psize = sizeL;
        }

        if (flagsL & VMQ_FREE)
        {
            vmAssert(flagsL == VMQ_FREE);

            *pflags = PAG_FREE;
        }
        else
        {
            /*
             * VMQ_RESERVED must be set
             * Either VMQ_ARENAPRV or VMQ_ARENASHR must be set, but not both
             */
            vmAssert(VMQ_ARENAPRV == VMAC_ARENAPRV &&
                     VMQ_ARENASHR == VMQ_ARENASHR);

            vmAssert((flagsL & VMQ_RESERVED) &&
              (vmPrvArenaReq(flagsL) || vmShrArenaReq(flagsL)))

            *pflags = 0;
            if (flagsL & PQ_COMMIT)
            {

                *pflags |= PAG_COMMIT;

                /*
                 * If the region is tiled, then "hide" any committed memory
                 * that is not accessible through the corresponding tiled
                 * segment.
                 */
                if (VMTiled(addr))
                {
                    if ((rc = SELGetDescInfo(SELLaToSel(addr + *psize - 1,
                                                            pPTDACur),
                                                 HOBNULL,
                                                 SSToDS(&desc))) == NO_ERROR)
                    {
                        if (addr >= desc.desc_laddr + desc.desc_cb)
                        {
                            /*
                             * The request covers committed memory beyond the
                             * end of the tiled segment.  Lie and say this
                             * memory is not committed.  We know that there
                             * is less than one committed page at the front of
                             * the requested range and the following pages in
                             * the object must be uncommitted.
                             */
                            vmAssert(*psize < PAGESIZE);

//                          *psize = oi.oi_va + oi.oi_cb - addr;
                            *psize = od.od_laddr +
                                     ((od.od_par)->ar_cpg << PAGESHIFT) - addr;
                            /*
                             * Since we're saying this is uncommitted, we must
                             * return the default object permissions.
                             */

                            if(!VMShrArena(addr))
                            {
//                              fl = (oi.oi_fsar & AR_PERMISSION)
//                                   >> VMAC_PSHIFT;
                                fl = ((od.od_par)->ar_f & AR_PERMISSION)
                                         >> VMAC_PSHIFT;


                            }
//                          else if(oi.oi_hco != HOBNULL)
                            else if((od.od_par)->ar_f & AR_HCO)
                            {
//                              fl = (vmCoMap(oi.oi_hco)->co_fb & CO_PERMMASK)
//                                   << CO_PERMSHFT;
                                fl = ((od.od_pco)->co_fb & CO_PERMMASK)
                                         << CO_PERMSHFT;
                            }
                            else
                            {
//                              fl = (oi.oi_fsob & OB_PERMMASK) >> OB_PERMSHFT;
                                fl = ((od.od_pob)->ob_fs & OB_PERMMASK)
                                         >> OB_PERMSHFT;
                            }
                            flagsL = (flagsL & ~PQ_PMASK) | (fl & ~PG_U);

                            *pflags &= ~PAG_COMMIT;
                        }
                        else
                        {
                            /*
                             * The request starts inside a tiled segment, and
                             * there may or may not be committed memory (less
                             * than one page) beyond the end of the segment.
                             * Truncate the size returned to the segment limit.
                             */
                            cb = (addr + *psize)-(desc.desc_laddr+desc.desc_cb);
                            vmAssert(cb < PAGESIZE);

                            if (cb > 0)
                            {
                                *psize -= cb;
                            }
                        }
                    }

                    /*
                     * SELGetDescInfo returns error only if the descriptor is
                     * invalid.
                     */
                    rc = NO_ERROR;
                }
            }
            if (flagsL & PQ_R)          *pflags |= PAG_READ;
            if (flagsL & PQ_W)          *pflags |= PAG_WRITE;
            if (flagsL & PQ_X)          *pflags |= PAG_EXECUTE;
            if (flagsL & PQ_GUARD)      *pflags |= PAG_GUARD;
            if (flagsL & VMQ_BASE)      *pflags |= PAG_BASE;
            if (flagsL & VMQ_ARENASHR)  *pflags |= PAG_SHARED;
        }
    }
    if (flsem != NULL)
    {
//      VMClearSem(oi.oi_hob);
        VMClearSem(vmPobToHob(od.od_pob));
    };
    vmAssert(pTSDCur->TSDcObjSem == 0);
    MMPHPostVM32ApiQueryMem()                   /* Performance Hook (20/A1) */
    return(rc);
}

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

/***EP  VM32ApiQueryMemState - Get page state information.
 *
 *      VM32ApiQueryMemState is the worker routine for the Dos32QueryMemState
 *      api.  This api queries the state of a range of memory.  The state and
 *      actual size queried are returned.  Note that this api is temporary
 *      until DosQueryProcStatus is implemented.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   addr - starting address of range to query (will be rounded
 *                     down to page boundary)
 *              psize - pointer to size in bytes of range to query (will be
 *                      rounded up to page size multiple).  Will receive the
 *                      actual size in bytes of the range having the state
 *                      returned in flags.
 *              pflags - pointer to a variable to receive the state of the
 *                       memory queried.
 *      RETURN  0 on success, flags contains:
 *                one of:
 *                  PAG_PRESENT     pages are present
 *                  PAG_NPIN        pages are not present, and in core
 *                  PAG_NPOUT       pages are not present, and not in core
 *                one of:
 *                  PAG_SWAPPABLE   pages are swappable
 *                  PAG_DISCARDABLE pages are discardable
 *                  PAG_RESIDENT    pages are resident
 *                  PAG_INVALID     pages are either free or not committed
 *              else error code
 */

ulong_t
VM32ApiQueryMemState(addr, psize, pflags)
ulong_t addr;
ulong_t *psize;
ulong_t *pflags;
{
    ulong_t flagsL = VMQ_STATE;
    ulong_t rc;

    if (*psize == 0) {
        return(ERROR_INVALID_PARAMETER);
    }

    if ((rc = VMQueryMem(addr, psize, HOBNULL, SSToDS(&flagsL))) == NO_ERROR) {
        *pflags = flagsL;
    }
    vmAssert(pTSDCur->TSDcObjSem == 0);
    return(rc);
}

#pragma code_seg()
#pragma code_seg("SWAP32_15", "CODE")
/***    calcTLMASize - utility routine to size TLMA in use
 *
 *      The idea is to find the last word in use so that during context
 *      switches, only the minimum number of double words need be copied.
 *      The assumption here is that there will be a minimum anoumt of
 *      fragmentation thus a simple block copy will be the most expedient.
 */


long calcTLMASize()
{
    int     i,j;
    uchar_t msk;

    for (i=TLMA_MAX_BME-1; i>=0; i--)
        if (pPTDACur->ptda_TLMABM[i] != 0)
                break;
    if (i < 0)
        return(0);
    msk = pPTDACur->ptda_TLMABM[i];
    for (j=8; (msk & 1)==0; msk>>=1, j--);
    return((i * 8) + j);
}

/***EP  VR32IAllocThreadLocalMemory - Allocate thread local memory
 *
 *
 *      ENTRY  tsef - thunksef
 *             size - size to allocate in dwords
 *             p    - pointer to return address
 *
 *      EXIT   return code
 *
 */

int cdecl VR32IAllocThreadLocalMemory(THUNKSEF tsef, ulong_t cd, ulong_t *p) {

   int rc;
   int i = 0, j;
   int found = 0;
   int index = 0;
   uchar_t mask = -1;
   uchar_t bme = 0, bme1;
   ULONG *pTLMA;

#if ((TLMA_MAX_ALLOC_SIZE) > TLMA_BITS_PER_BM_ENTRY)
#error Max allocation size does not fit in 1 bit map entry
#endif

   if ((cd == 0) || (cd > TLMA_MAX_ALLOC_SIZE))
      return (ERROR_INVALID_PARAMETER);

   mask <<= TLMA_BITS_PER_BM_ENTRY - cd;
   bme = pPTDACur->ptda_TLMABM[i];

   while (i < TLMA_MAX_BME) {
      for (j = 0, bme1 = bme; (j + cd - 1) < TLMA_BITS_PER_BM_ENTRY; j++,
         index++, bme1 <<= 1) {
            if (!(bme1 & mask)) {
               found = 1;
               break;
            }
      }

      if (found)
         break;

      i = index / TLMA_BITS_PER_BM_ENTRY;
      j = index % TLMA_BITS_PER_BM_ENTRY;
      bme = pPTDACur->ptda_TLMABM[i];
      if (j > 0) {
         bme <<= j;
         bme |= pPTDACur->ptda_TLMABM[++i] >> (TLMA_BITS_PER_BM_ENTRY - j);
      }
   }

   if (found) {

      vmAssert(index > 0); // Index 0 is reserved for PM/Kernel Communication.

      rc = TKSuDWord(pTLMA = (SEL_TASKINFOLADDR + LIS_LEN) +
         (index * sizeof(ulong_t)), p);

      if (rc == NO_ERROR) {
         i = index / TLMA_BITS_PER_BM_ENTRY;
         j = index % TLMA_BITS_PER_BM_ENTRY;
         mask >>= j;
         pPTDACur->ptda_TLMABM[i] |= mask;
         if ((j += cd) > TLMA_BITS_PER_BM_ENTRY) {
            j -= TLMA_BITS_PER_BM_ENTRY;
            mask = (uchar_t)(-1 << (TLMA_BITS_PER_BM_ENTRY - j));
            pPTDACur->ptda_TLMABM[++i] |= mask;
         }
         pPTDACur->ptda_TLMASizeMap[index] = (uchar_t)cd;
         pPTDACur->ptda_TLMA = calcTLMASize();          // mark TLMA in use
         memset((char *)pTLMA, 0, cd * sizeof(ulong_t));// guarentee zero filled
      }
   }
   else
      rc = ERROR_NOT_ENOUGH_MEMORY;

   return(rc);
}
#pragma code_seg()
#pragma code_seg("VMSWAP_TEXT", "CODE")


/***EP  VR32IFreeThreadLocalMemory - Free thread local memory
 *
 *
 *      ENTRY  tsef - thunksef
 *             p    - address to free
 *
 *      EXIT   return code
 *
 */

int cdecl VR32IFreeThreadLocalMemory(THUNKSEF tsef, ulong_t p) {

   int rc = ERROR_INVALID_ADDRESS;
   int index;
   int cd;
   int i, j;
   uchar_t mask = -1;

   // First DWORD of TLMA cannot be free'd. It is reserved for
   // PM/Kernel Communication.

   if ((p >= (SEL_TASKINFOLADDR + LIS_LEN + sizeof(ulong_t))) &&
      (p <= (SEL_TASKINFOLADDR + LIS_LEN + TLMA_LEN - sizeof(ulong_t))) &&
         (!(p & (sizeof(ulong_t) - 1)))) {

         index = (p - (SEL_TASKINFOLADDR + LIS_LEN)) / sizeof(ulong_t);

         vmAssert(index > 0); // Index 0 is reserved for PM/Kernel Communication.

         if ((cd = pPTDACur->ptda_TLMASizeMap[index]) != 0) {

            i = index / TLMA_BITS_PER_BM_ENTRY;
            j = index % TLMA_BITS_PER_BM_ENTRY;
            mask <<= TLMA_BITS_PER_BM_ENTRY - cd;
            mask >>= j;
            pPTDACur->ptda_TLMABM[i] &= ~mask;
            if ((j += cd) > TLMA_BITS_PER_BM_ENTRY) {
               j -= TLMA_BITS_PER_BM_ENTRY;
               mask = (uchar_t) (-1 << (TLMA_BITS_PER_BM_ENTRY - j));
               pPTDACur->ptda_TLMABM[++i] &= ~mask;
            }
            pPTDACur->ptda_TLMASizeMap[index] = 0;
            pPTDACur->ptda_TLMA = calcTLMASize();

            rc = NO_ERROR;
         }
   }

   return(rc);
}

/***EP  VR32ISYSCTL - General purpose PRIVATE system interface
 *
 *
 *      ENTRY  tsef     - thunksef
 *             function - identify function
 *             p        - pointer parameter
 *
 *      EXIT   return code
 *
 */

#include "hybernat.h"

extern int cdecl     VDMINT10CreateMiniVDM (void);     //  @V3.2SEN00
extern void cdecl    VDMINT10DestroyMiniVDM(void);     //  @V3.2SEN00
extern int cdecl     VDMINT10IssueRequest(ULONG *);    //  @V3.2SEN00
extern int cdecl     VDMINT10ThawRequest(ULONG *);     //  @V3.2SEN00

int cdecl VR32ISysCtl(THUNKSEF tsef, ulong_t func, ulong_t *p) {
        char buf[256];
        int rc;
        ulong_t param;

        ulong_t size;
        ulong_t laddr;

        extern int hyberPageOut();
        extern int FSD_FlushBuf(short hVPB, short usDiscard);
        extern ulong_t hyberFileCheck(ulong_t param);            /* 150219 */
        extern ulong_t fhybernation;  /* TRUE = hybernation allowed 152533 */
        extern void NotifyDDSaveRestore(ulong_t param) ;         /* 150806 */
        extern ULONG CpuType;                                    /* 152914 */
        extern ULONG IODELAYCOUNT;                               /* 152914 */

        if ( !fhybernation ) {   /* disallow all hybernation calls  152533 */
           if ( (func >= SYS_HY_START) &&                        /* 152533 */
                (func <= SYS_HY_END) ) {                         /* 152533 */
              return(ERROR_ACCESS_DENIED);                       /* 150219 */
           }                                                     /* 150219 */
        }                                                        /* 150219 */

        switch (func) {
        case    1:
                pgAssert(smDelaySwap);
                if (!smDelaySwap)
                        return(ERROR_INVALID_ACCESS);
                if (TKFuBuffz(NULL, SSToDS(buf), (char *)p, sizeof(buf), 0))
                        return(ERROR_INVALID_ADDRESS);
                SMReinit(SSToDS(buf));
                if (smInitialized != 2)
                        return(ERROR_OPEN_FAILED);
                smDelaySwap = 0;
                break;
        case    2:  // Is process being debugged
                if (pPTDACur->ptda_pdcb != NULL)
                   return (TRUE);
                else
                   return (FALSE);
                break;
        /*
        ** beginning of @V3.2SEN00
        ** PRIVATE VIDEOPMI-kernel interface.
        ** function 3 is querying if the kernel supports the INT10 miniVDM.
        ** function 4 is to create an INT10 miniVDM
        ** function 5 is to destroy the INT10 miniVDM
        ** function 6 is to issue a new INT10 request (will block until request finished).
        ** function 7 is to thaw a full-blown(not miniVDM) private VDM.
        ** Synchronization of functions is the responsability of VIDEOPMI.
        */
        case   3:
               return(NO_ERROR);
        case   4:
               return(VDMINT10CreateMiniVDM());
        case   5:
               VDMINT10DestroyMiniVDM();
               return(NO_ERROR);
        case   6:
               return(VDMINT10IssueRequest(p));
        case   7:
               return(VDMINT10ThawRequest(p));
        /*
        ** end of @V3.2SEN00
        */

        case    8:  // returns config.sys file from the d1 diskette
                /***
                 *  This function supports getting the config.sys file image
                 *  that was used to boot with when booting from diskette.
                 *  If the machine is not booted from diskette, the function
                 *  will return ERROR_BAD_ENVIRONMENT.  This function is needed
                 *  to eliminate the re-insertion of the D1 diskette for CDROM
                 *  and lan installations.
                 *
                 *  the returned data has the structure of:
                 *
                 *   struct config_rtn_s {
                 *      ULONG size;    // size of config.sys file
                 *      UCHAR buf[1];  // buf for config.sys file image
                 *   } *p;
                 *
                 *  twodiskboot (set by sysinit) will either be the selector
                 *  that holds the memory image of the config.sys file (when
                 *  booting from diskette) or 0
                 */
                laddr = SELConvertToLaddr(twodiskboot);
                if (laddr == -1) return(ERROR_BAD_ENVIRONMENT);
                /*
                 * get the size of the config.sys file
                 */
                rc = SELGetSize(laddr, twodiskboot, 0, 0, SSToDS(&size) );
                if (rc != NO_ERROR) return(rc);
                /*
                 * set size in the return buffer
                 */
                if ( TKSuDWordNF(size,p) != NO_ERROR)
                     return(ERROR_INVALID_PARAMETER);
                /*
                 * copy config.sys memory image to user's buffer
                 */
                if ( TKSuBuff((char *)p+4, (char *)laddr, size, 0) != NO_ERROR)
                     return(ERROR_INSUFFICIENT_BUFFER);
                break;

        case   9:                                                  /* 152914 */
                if ((rc = TKSuDWordNF(CpuType, p)) != NO_ERROR)
                        return(rc);
                return(NO_ERROR);

        case   10:                                                 /* 152914 */
                if ((rc = TKSuDWordNF(IODELAYCOUNT, p)) != NO_ERROR)
                        return(rc);
                return(NO_ERROR);

        case SYS_RESETSTAT:            /* FUNCTION 11 */

                if ((rc = TKFuDWord(SSToDS(&param), p)) != NO_ERROR)
                        return(rc);
//156773        if (param == 0)
//156773                hyberFreeSwapper();
                hyberStat = param;
                break;

        case    SYS_HIBERNATE:         /* FUNCTION 12 */

                if ((rc = TKFuDWord(SSToDS(&param), p)) != NO_ERROR)
                        return(rc);
                return(hybernate(param));

        case    SYS_FREEZE_THAW_SYS:   /* FUNCTION 13 */

                if ((rc = TKFuDWord(SSToDS(&param), p)) != NO_ERROR) // 150281
                        return(rc);                                  // 150821
                return(hyberFreezeSys(param));                       // 150281

        case    SYS_GETHYBERSTAT:      /* FUNCTION 14 */

                return(hyberFreezeSys());

        case    SYS_SYNCFS:            /* FUNCTION 15 */

                if ((rc = TKFuDWord(SSToDS(&param), p)) != NO_ERROR)
                        return(rc);
                rc = FSD_FlushBuf(0, (ushort_t)param);
                return(rc);
                break;

        case    SYS_SWAPOUT:           /* FUNCTION 16 */

                return(hyberPageOut());

        case    SYS_HYBEROK:           /* FUNCTION 17 */

                if ((rc = TKFuDWord(SSToDS(&param), p)) != NO_ERROR)
                        return(rc);
                return(hyberOK(param));

        case    SYS_SWAPPATH:          /* FUNCTION 18 */

                if ((rc = TKFuDWord(SSToDS(&param), p)) != NO_ERROR)
                        return(rc);
                strcpy((char *)param, getSwapperPath());
                break;

        case    SYS_CHKSTICKY:         /* FUNCTION 19 */

                if ((rc = TKFuDWord(SSToDS(&param), p)) != NO_ERROR)
                        return(rc);
                return(hyberFileCheck(param));

        case    SYS_SAVERESTORE:       /* FUNCTION 20 */    /* 150806 Start */

                if ((rc = TKFuDWord(SSToDS(&param), p)) != NO_ERROR)
                        return(rc);
                if (param > 1)      /* Validate parameter */
                        return(ERROR_INVALID_PARAMETER);
                NotifyDDSaveRestore(param);                 /* 150806 End   */
                break;

        case    21:                    /* Memory decommit */      /* 153480 */

                return(rc = VMFreeAllContext(p));                 /* 153480 */

        default:
                return(ERROR_INVALID_FUNCTION);
        }
        return(NO_ERROR);
}
