/*static char *SCCSID = "@(#)vminfo.c   6.5 91/11/05";*/
#define SCCSID  "@(#)vminfo.c   6.5 91/11/05"


/*
 *       IBM/Microsoft Confidential
 *
 *       Copyright (c) IBM Corporation 1987, 1989
 *       Copyright (c) Microsoft Corporation 1987, 1989
 *
 *       All Rights Reserved
 *
 *  Modifications:
 *  Defect 100988 10/14/94 ChgTeam - Added check for sentinel to VMQueryMem
 *                         to avoid trap e in VMGetObPermOd when DosQueryMem
 *                         is run against an address which is a sentinel
 *                         in the shared arena and has been allocated by
 *                         another process.
 */


#define INCL_DOSERRORS
#define INCL_OS2STD
#include <os2.h>

#define INCL_DOSP
#include <os2p.h>

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

#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <mi.h>
#include <pgexport.h>
#include <vmexport.h>
#include <vmlocal.h>
#include <selxport.h>
#include <tkexport.h>
#include <infoseg.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#include <stdlib.h>

ulong_t VMTrap2FindArena( VMHOB hob, struct ptda_s  **ppptda);

#define     VMCP_NOYIELD    (1 << 9)    /* Copy 512 dwords without yielding */
#define     VMCP_BLOCKSIZE  (1 << 10)   /* Copy 1Kb dwords between each yield*/


/***OP  VMGetMemInfo - Return handle size, and state of memory object
 *
 *      WARNING:  This function is obsolete.  Use VMGetOd instead
 *
 *      This function is returns information on the given object after
 *      waiting for it to stabilize. It will also obtain object semaphore
 *      if requested. If noblock option is specified and the object is
 *      not stable, (semaphore busy), it will return error.
 *      WARNING: This function must remain in resident code.
 *
 *      VMGetMemInfo(ulong fl, ulong laddr, VMHOB hptda, VMOI *pob, char fb)
 *
 *      ENTRY   fl      - VMGetSem flags
 *              laddr   - linear address of memory object
 *              hptda   - ptda handle (required for user area laddr only)
 *              poi     - address to copy memory object info
 *              fb      - VMMI_CHECKUVIRT return error if UVIRT object
 *      RETURN  int     - error code (NO_ERROR if no error)
 *                        if ERROR_DIRECT_ACCESS_HANDLE
 *                          Memory object is a PhysToUVirt
 *
 *      PseudoCode:
 *
 *      do {
 *          call VMObjectInfo
 *          if (error) return (error code)
 *      }
 *      while {
 *          VMGetSem returns ERROR_BLOCKED
 *      }
 *      If (error), return (error code)
 *      If (check for UVIRT && UVIRT object)
 *          if (HandleSemaphore obtained) call VMClearSem
 *          return(ERROR_DIRECT_ACCESS_HANDLE)
 *      Endif
 *      return (NO_ERROR)
 */

// int         VMGetMemInfo(fl, laddr, hptda, poi, fb)
// ulong_t     fl;                 /* VMGetSem flags */
// ulong_t     laddr;              /* linear address of object */
// VMHOB       hptda;              /* handle to ptda */
// register VMOI *poi;             /* address to copy memory object info */
// char        fb;             /* VMMI_CHECKUVIRT means return error if UVIRT */
// {
//     poi->oi_va = laddr;
//     poi->oi_hobptda = hptda;
//     if(fb & VMMI_CHECKUVIRT)
//       {
//         fl |= VMOD_CHECK_UVIRT;
//       }
//     return(VMGetObjectInfo(fl | VMOI_VA, poi));
// }

/***EP  VMGetLaddr  - Return the linear address of a memory object given its
 *                      handle.
 *
 *      VMGetLaddr returns the linear address of a memory object given
 *      its handle. The passed handle must correspond to a real memory
 *      object.
 *      WARNING: This function must remain in resident code.
 *
 *      ENTRY   hob - handle to object record
 *              ppladdr - Pointer to location to write linear address to
 *      RETURN  int -       Error code  (NO_ERROR if no error)
 *
 *      VMGetLaddr(VMHOB hob, ulong_t *pladdr)
 *
 *      PseudoCode:
 *
 *      Call VMHandleMap            ; internal errors if pseudo handle
 *      Set har = pob->ob_har       ; get first arena record
 *      While (har != 0) {
 *          Call vmArMap            ; convert har to par
 *
 *          If ( not an alias )
 *              return (vmArVa)     ; return address from AR
 *          Endif
 *          Set har = par->ar_harlink
 *      }
 *      return (ERROR_NOT_FOUND)
 */
int            VMGetLaddr(hob, pladdr)
VMHOB          hob;            /* Handle to object record */
ulong_t        *pladdr;        /* Location to write address of object */
{
    register    VMHAR har;      /* handle to arena record */
    register    VMAR *par;      /* pointer to arena record */

    har = (VMHandleMap(hob))->ob_har;
    while (har) {
        if (!(vmAliasAr(par=vmArMap(har))))     /* skip aliases */
            break;
        har = par->ar_harlink;
    }
    *pladdr = (ulong_t) vmArVa(par);        /* either nonalias or only alias*/
    return (NO_ERROR);
}

/***EP  VMTrap2FindArena - given hob : find starting linear address of arena
 *                                     and the pptda that it resides in
 *
 *      This function determines whether a hob is in the private shared
 *      or system arena.  If the hob is in a private arena it returns the
 *      base linear address specified by the hob's har.  If shared or system
 *      it returns a -1.  This function is provided as a special interface
 *      for Trap2 handling.
 *      WARNING: This function must remain in resident code.
 *
 *      ulong_t VMTrap2FindArena(VMHOB hob)
 *
 *      ENTRY           hob             - handle of object record
 *                      ppptda          - pointer to pointer to ptda
 *
 *      RETURN          int             - returns -1 if shared of system arena
 *                                        otherwise return starting virtual
 *                                        address of hob in question
 *      CALLS           none
 *
 *      WARNING         This code must not block
 *
 *      EFFECTS         None
 */

ulong_t VMTrap2FindArena(hob,ppptda)
VMHOB hob;               /* handle to object */
struct ptda_s  **ppptda;  /* pointer to Pointer to PTDA */
{
   VMAH    *pah;           /* pointer to Arena header  */
   ulong_t laddr;          /* linaddr of this hob */
   VMHOB   hptda;          /* handle of ptda */
   struct ptda_s  *pptda;  /* Pointer to PTDA */

   if ( ((VMObjHandleInfo(hob, SSToDS(&laddr),SSToDS(&hptda))) == NO_ERROR) &&
         hptda != 0) {
      pptda = VMPseudoHandleMap(hptda);
      pah   = &(pptda->ptda_ah);    // get pointer to arena header for context

      /*
       *    Determine if the laddr fails within the private arena
       *      return the base linear address and pptda if it deos
       */
      if (laddr < pah->ah_laddrMax) {
         *ppptda = pptda;
         return(laddr);
      }
   }
   /*
    *   if not private must be in the shared or system region
    *    return -1 in either case
    */
   return(-1);
}



/***EP  VMObjHandleInfo - Find context and address of the given object handle.
 *
 *      VMObjHandleInfo returns the virtual address and context of the
 *      object given its handle. If object is in system area, 0 is returned
 *      for context.
 *      WARNING: This procedure walks through arena records without taking
 *      object semaphore, therefore it must remain in resident code.
 *
 *      ENTRY   hob     - handle to object record
 *              pladdr  - Location to write the address
 *              phptda  - Location to write the context
 *      RETURN  int     - Error code (NO_ERROR if no error)
 *
 *      VMObjHandleInfo(VMHOB hob, ulong *pladdr, VMHOB *phptda)
 *
 *      PseudoCode
 *
 *      Call VMHandleVerify
 *      If (error), InternalError
 *      Set har = pob->ob_har
 *      While (har != 0)
 *          Convert har to par
 *          if (not an alias) break
 *      Return ar_hobptda and ar_ipg << PAGESHIFT
 */
int        VMObjHandleInfo(hob, pladdr, phptda)
VMHOB      hob;                    /* Object handle */
ulong_t    *pladdr;                /* Location to return address */
VMHOB      *phptda;                /* Location to return context */
{
    int     rc;                     /* return code */
    register VMHAR har;             /* Arena record handle */
    register VMAR  *par;            /* Pointer to Arena record */
    VMOB    *pob;                   /* Pointer to object record */

    if ((rc = VMHandleVerify(hob, 0, SSToDS(&pob))) != NO_ERROR)
        return(rc);
    har = pob->ob_har;
    vmAssert(har);
    while (har) {
        if (!(vmAliasAr((par=vmArMap(har))))) break;
        har = par->ar_harlink;
    }
#ifdef VMSTRICT
    if (har == HARNULL) {   /* Only if CS alias, so it must be private */
        vmAssert((pob->ob_fs & OB_SHARED) == 0);
        vmAssert(!VMShrArenaPg(par->ar_ipg) && !VMSysArenaPg(par->ar_ipg));
    };
#endif
    *pladdr = par->ar_ipg << PAGESHIFT;
    if (VMSysArena(*pladdr))
        *phptda = 0;
    else {
        if (!(pob->ob_fs & OB_SHARED))
            *phptda = pob->ob_hobowner;
        else {
            if (par->ar_f & AR_HCO)
//              *phptda = (vmCoMap(par->ar_hco))->co_hobptda;
                *phptda = (vmCoMap(VMHcoAr(*par)))->co_hobptda;
            else
                *phptda = par->ar_hobptda;
        };
    };
    return (NO_ERROR);
}


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

void    VMQueryArenaAvailMem(fl, pcbLinAvail)
ulong_t                 fl;             /* Flags */
ulong_t                 *pcbLinAvail;   /* Return pointer */
{
  ulong_t               cbLinOutside;   /* Avail linear mem outside range
                                         * between min and max reservations
                                         * that can be allocated in given
                                         * arena
                                         */
  ulong_t               cbLinWithin;    /* Avail linear mem within range
                                         * between min and max reservations
                                         */
  ulong_t               laddrMaxPrv;    /* Upper bound to which private
                                         * arena can grow up.
                                         */
  ulong_t               laddrMinShr;    /* Lower bound to which shared
                                         * arena can grow down.
                                         */
  VMAH                  *pah;           /* Arena header */
  VMAR                  *par;           /* Lower arena record */
  VMAR                  *parNext;       /* Upper arena record */
  VMAR                  *parSen;        /* Sentinel */

  /*
   *  Verify flags.  Private or Shared arena must be specified.
   */

  vmAssert(vmPrvArenaReq(fl) || vmShrArenaReq(fl));

  /*
   *  Deal with private arena request.
   */

  if(vmPrvArenaReq(fl))
    {
      pah = &(pPTDACur->ptda_ah);

      /*
       *  Calculate amount of memory between end of highest reservation
       *  in current task's private arena and min(start of lowest reservation
       *  in shared arena, absolute max. limit of private arena growth).  Add
       *  in amount of memory from minimal base of private arena up to base of
       *  lowest reservation in that arena.
       */

      laddrMaxPrv = (ahvmShr.ah_laddrMin < VMAT_PT_laddrAbsMax) ?
                        ahvmShr.ah_laddrMin : VMAT_PT_laddrAbsMax;
      cbLinOutside = (laddrMaxPrv - pah->ah_laddrMax) +
                     (pah->ah_laddrMin - VMAT_ST_laddrAbsMin);
    }

  /*
   *  Deal with shared arena request.
   */

  else
    {
      pah = &ahvmShr;

      /*
       *  Calculate amount of memory between end of highest reservation
       *  across ALL private arenas and start of lowest reservation in shared
       *  arena.  Add in amount of memory from end of highest reservation
       *  in shared arena up to upper absolute limit of shared arena.
       *  NOTE: To calculate the first of these quantities, call
       *  vmRecalcShrBound() to obtain the new minimum possible shared
       *  arena bound (equals the maximum limit for private arena growth).
       */

      vmRecalcShrBound(NA, SSToDS(&laddrMinShr));
      cbLinOutside = (ahvmShr.ah_laddrMin - laddrMinShr) +
                     (VMAT_ST_laddrAbsMax - ahvmShr.ah_laddrMax);
    }

  /*
   *  Walk the arena (private or shared) to compute the available free
   *  linear space between the lowest and highest reservations in the
   *  arena.
   */

  cbLinWithin = 0;

  par = parSen = pah->ah_parSen;
  parNext = vmArMap(par->ar_harnext);

  while(parNext != parSen)
    {
      par = parNext;

      /*
       *  Map next arena record.  If it is the sentinel, we're done
       *  since this loop is only computing space between regular
       *  arena records.
       */

      parNext = vmArMap(par->ar_harnext);
      if(parNext == parSen)
        {
          break;
        }
      vmAssert(parNext->ar_ipg >= par->ar_ipg + par->ar_cpg);
      cbLinWithin +=
        (parNext->ar_ipg - (par->ar_ipg + par->ar_cpg)) << PAGESHIFT;
    }

  /*
   *  Return result to caller
   */

  *pcbLinAvail = cbLinOutside + cbLinWithin;
}


/***EP  VMQueryMem - Query page granular permissions and attributes
 *
 *      This function returns page granular information on a range of
 *      virtual memory.  If VMQ_ACCESS is specified, the access permiss-
 *      ions (read, write, execute, guard) and the allocation attributes
 *      (reserved, committed, private, shared) are returned.  If
 *      VMQ_STATE is specified, the page state (present, not-present
 *      in core, not-present not in core) and page type (swappable,
 *      discardable, resident, invalid) are returned.  The specified range
 *      does not have to be contained entirely within one object, however
 *      it will be truncated if necessary to a region which does not cross
 *      an object boundary and in which all pages are of the same state.
 *      Regions covered by objectless arena records (eg. call gates) are
 *      treated as non-reserved memory.
 *
 *      Note that the virtual address is rounded down to a page boundary
 *      and the size is rounded up to a page multiple.
 *
 *      The section of this function where VMGetMemInfo fails, arena records
 *      of different objects are walked, and this walk should be
 *      completed without blocking.
 *
 *      int VMQueryMem(ulong_t laddr, ulong_t *pcb, VMHOB hobPTDA, ulong_t &pfl)
 *
 *      ENTRY           laddr           - Region's linear address
 *                      pcb             - Pointer to region's size in bytes
 *                      hobPTDA         - PTDA handle
 *                      pfl             - Pointer to entry and return flags
 *
 *                                Entry:
 *                                VMQ_ACCESS
 *                                  Query access permissions and allocation
 *                                  attributes
 *                                Return:
 *                                VMQ_FREE
 *                                  The specified address is not located inside
 *                                  any object, ie. the memory region is free
 *                                VMQ_BASE
 *                                  The specified address is the base address
 *                                  of an object
 *                                VMQ_RESERVED
 *                                  The region is reserved
 *                                VMQ_ARENAPRV
 *                                  The object is in the private arena
 *                                VMQ_ARENASHR
 *                                  The object is in the shared arena
 *                                PQ_COMMIT
 *                                  The object is committed
 *                                PQ_X
 *                                  Execute access to the committed range
 *                                  is allowed.
 *                                PQ_R
 *                                  Read access to the committed range
 *                                  is allowed
 *                                PQ_W
 *                                  Write access to the committed range
 *                                  is allowed
 *                                PQ_GUARD
 *                                  Access to the committed range of pages
 *                                  will cause a "guard page entered"
 *                                  condition in the subject process.
 *
 *                                Entry:
 *                                VMQ_STATE
 *                                  Query page state and type
 *                                Return:
 *                                PQ_P
 *                                  Pages are present
 *                                PQ_NPI
 *                                  Pages are not-present, and in core
 *                                PQ_NPNI
 *                                  Pages are not-present, and not in core
 *                                PQ_SWAPPABLE
 *                                  Pages are swappable
 *                                PQ_DISCARDABLE
 *                                  Pages are discardable
 *                                PQ_RESIDENT
 *                                  Pages are resident
 *                                PQ_INVALID
 *                                  pages are invalid
 *
 *      RETURN          int             - Result code (NO_ERROR = success)
 */

int VMQueryMem(laddr, pcb, hobPTDA, pfl)
register ulong_t        laddr;          /* Linear address  */
ulong_t                 *pcb;           /* Pointer to region size in bytes */
VMHOB                   hobPTDA;        /* PTDA handle */
ulong_t                 *pfl;           /* Pointer to flags */
{
  ulong_t               laddrMax;       /* Max bound of queried region */
  register ulong_t      flOd;           /* VMGetOd flags */
  VMOD                  od;             /* Object data */
  register VMOD         *pod = SSToDS(&od);
  int                   rc;             /* Result code */

  /*
   *  Round region size and base to limits for enclosing page-granular
   *  region.
   */

  *pcb = VMRegionSize(laddr, *pcb);
  laddr = VMRegionBase(laddr);
  od.od_laddr = laddr;
  od.od_pPTDA = VMResGetPTDAAddr(hobPTDA);

  /*
   *  If not in the system arena the context is relevant; if the current
   *  task is a VDM, the context must be the current one.
   */

  vmAssert(VMSysArena(laddr) ||
    vmCurrTask(od.od_pPTDA) ||
    !(vmVDMTask(od.od_pPTDA)));

  /*
   *  Search for containing or lower reservation, returning sentinel for
   *  upper arena if given address lies in between arenas.  If an object
   *  is found, get object data taking its handle semaphore to ensure
   *  object stability.
   */

  if(laddr >= ahvmShr.ah_laddrMax)
    {
      flOd = VMAC_ARENASYS;
    }
  else if(laddr >= od.od_pPTDA->ptda_ah.ah_laddrMax)
    {
      flOd = VMAC_ARENASHR;
    }
  else flOd = VMAC_ARENAPRV;
  if((rc = VMGetOd(flOd | VMOD_NEAREST_BELOW | VMGS_TAKESEM, SSToDS(&od))) ==
    NO_ERROR)
    {
      /*
       *  An object below or containing the given base linear address
       *  was found.  Check for containing object.
       */

      /*  Defect 100988 - If VMGetOd returned the arena record for a
       *  sentinel in the shared arena, do not call vmQueryObject,
       *  because the pointer to the object record is null.
       */
      if(vmWithinAr(laddr, od.od_par) &&
        (!VMShrArena(laddr) || !vmSentAr(od.od_par)))                /*100988*/
        {
          /*
           *  The range begins inside an object.  Query object permissions
           *  and truncate the range to fit entirely within the object if
           *  necessary.
           */

          rc = vmQueryObject(pod, laddr, pcb, pfl);
          VMClearSem(VMHobOd(od));
          return(rc);
        }

      /*
       *  Object below given base address found.  Release semaphore
       *  since we will not be querying the object.  Set the starting
       *  point of the search as the next arena record.
       */

      vmAssert(vmRegAr(od.od_par) || vmSentAr(od.od_par));
      if(vmRegAr(od.od_par))
        {
          if(od.od_par->ar_hob != HOBNULL)
            {
              VMClearSem(VMHobOd(od));
            }
          od.od_par = vmArMap(od.od_par->ar_harnext);
        }
      else
        {
          /*
           *  Sentinel returned.  Point at first reservation in arena.
           */

          od.od_par = vmArMap(od.od_par->ar_harnext);
        }

      /*
       *  BUGBUG IDB 9/20/90 - In the case where the given linear
       *  address lies within a shared arena object not valid in the
       *  current context, we also come here.  The code prior to the
       *  introduction of VMGetOd selected the next arena record
       *  above and returned as the byte count the distance between the
       *  given linear address and the base of that object.  I do so
       *  here for compatibility.
       */

      if(VMShrArena(laddr) && vmWithinAr(laddr, od.od_par))
        {
          od.od_par = vmArMap(od.od_par->ar_harnext);
        }
    }

  else
    {
      /*
       *  An error was returned.
       */

      if(rc == ERROR_NO_OBJECT)
        {
          od.od_par = vmArMap(od.od_par->ar_harnext);
          rc = NO_ERROR;
        }
      else
        {
          return(rc);
        }
    }

  /*
   *  Scan through memory, crossing arena boundaries if necessary,
   *  searching for a valid object (one that is not "objectless").
   */

  for(;;)
    {
      /*
       *  If the arena record we're on is "objectless" (eg. a call
       *  gate), scan until we find an object or until we hit the
       *  end of the arena.
       */

      while(od.od_par->ar_hob == HOBNULL && !vmSentAr(od.od_par))
        {
          od.od_par = vmArMap(od.od_par->ar_harnext);
        }

      if(!vmSentAr(od.od_par))
        {
          laddrMax = od.od_par->ar_ipg << PAGESHIFT;
          break;
        }

      /*
       *  We hit a sentinel.  Move to the next arena, or break if
       *  we're in the last arena.
       */

      if(od.od_pah == &(od.od_pPTDA->ptda_ah))
        {
          od.od_par = vmArMap(ahvmShr.ah_parSen->ar_harnext);
          od.od_pah = &ahvmShr;
        }
      else if(od.od_pah == &ahvmShr)
        {
          od.od_pah = &ahvmSys;
          od.od_par = vmArMap(ahvmSys.ah_parSen->ar_harnext);
        }
      else
        {
          vmAssert(od.od_pah == &ahvmSys && od.od_par == ahvmSys.ah_parSen);
          laddrMax = ahvmSys.ah_pat->at_laddrAbsMax;
          break;
        }
    }

  /*
   *  We have the arena record of the object (or system arena sentinel)
   *  immediately following the specified address.  Compute the size of
   *  the free space between the two.
   */

  *pcb = laddrMax - laddr;

  if(*pfl & VMQ_STATE)
    {
      *pfl = PQ_INVALID;
    }
  else
    {
      *pfl = VMQ_FREE;
    }
  return(rc);
}


/***EP  VMIsPrivatized - Return true if given memory is private/privatized
 *
 *      VMIsPrivatized returns true if given address and context
 *      belongs to a private/privatized object.
 *      WARNING: This procedure walks arena and context records without
 *      obtaining the object sempahore. Therefore it must remain in resident
 *      code.
 *
 *      ENTRY  laddr    - Virtual address
 *             hptda    - Context
 *
 *      EXIT    TRUE if private memory/FALSE if shared
 *
 *      int     VMIsPrivatized(ulong_t laddr, VMHOB hptda)
 *
 */
int     VMIsPrivatized(laddr, hptda)
ulong_t laddr;
VMHOB   hptda;
{
//  VMOI    oi;
//  register VMOI  *poi = SSToDS(&oi);

             VMOD    od;
    register VMOD    *pod = SSToDS(&od);
    int     rc;

//  poi->oi_va = laddr;
//  poi->oi_hobptda = hptda;
//  rc = VMGetObjectInfo(VMOI_VA, poi);

    pod->od_laddr = laddr;
    pod->od_pPTDA = (hptda == HOBNULL) ? pPTDACur : VMGetPTDAAddr(hptda);
    rc = VMGetOd(NA, pod);
    vmAssert(rc == NO_ERROR);

    /* If private memory, or privatized memory, return TRUE */
//  if (VMPrvOi(*poi) ||
//      ((poi->oi_fsar & AR_HCO) && vmPrivCo((vmCoMap(poi->oi_hco)))))

    if(VMPrvOd(*pod) ||
           (((pod->od_par)->ar_f & AR_HCO) && vmPrivCo(pod->od_pco)))
    {
        return(TRUE);
    }
    return(FALSE);
}


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

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


/***EP  VMCopy - Copy specified number of bytes from one memory location
 *                  to another.
 *
 *      VMCopy copies given number of bytes from one location to another,
 *      yielding to time critical threads when necessary. It will always
 *      yield, unleass the number of bytes to be copied are less than 2Kb.
 *
 *      The parameters are not verified. The caller must ensure that
 *      the source and destination memory are valid and will remain
 *      valid during yields. (i.e. by taking the object semaphore)
 *
 *      Prospective callers of this procedure should first investigate
 *      PGMove, which is a much faster version of moving memory to a
 *      different address range.
 *      WARNING: This function is in swappable code.
 *
 *      ENTRY   cb      - Number of bytes to copy
 *              plsrc   - Location to copy from
 *              pldest  - Location to copy to
 *      RETURN  NONE
 *
 *      VMCopy (ulong cb, void *plsrc, void *pldest)
 *
 *      PseudoCode
 *
 *      Set dwordCount = cb >> 2    ; Copy in dwords
 *      Set oddBytes = cb & 3       ; No. of bytes remaining
 *      If (cb <= 2Kb)
 *          do (dwordCount) times
 *              (*pldest)++ = (*plsrc)++
 *      Else
 *          While (dwordCount)
 *              set cbcopy = min(dwordCount, 4Kb)
 *              Do (cbcopy) times
 *                  (*pldest)++ = (*plsrc)++
 *              TKTCYield
 *              set dwordCount -=cbcopy
 *      Endif
 *      if (oddBytes)
 *          uchar *pcbsrc = plsrc; ; define pointers to a character, initialize
 *          uchar *pcbdest = pldest;
 *          Do (oddBytes) times
 *              (*pcbsrc)++ = (*pcbdest)++
 *      endif
 */
void        VMCopy(cb, plsrc, pldest)
ulong_t     cb;                 /* No. of bytes to copy */
register    ulong_t *plsrc;     /* Location to copy from */
register    ulong_t *pldest;    /* Location to copy to */
{
    ulong_t     cbodd = cb & 0x3;   /* Remainder bytes after dword copy */
    register    ulong_t cbeven = cb >> 2;   /* No. of dwords to copy */
    ulong_t     cbcopy;             /* No. of bytes to copy without yielding*/
    int         i;
    uchar_t     *pbsrc;             /* Pointer to a byte (used in odd copies)*/
    uchar_t     *pbdest;            /* Pointer to a byte (used in odd copies)*/

    if (cbeven <= VMCP_NOYIELD) {   /* if <= 2Kb bytes to copy */
        for (i = 0; i < cbeven ; i++)
            *pldest++ = *plsrc++;
    }
    else {          /* Yield between copies */
        while (cbeven > 0) {
            cbcopy = (cbeven < VMCP_BLOCKSIZE) ? cbeven : VMCP_BLOCKSIZE;
            cbeven -= cbcopy;
            for (i = 0 ; i < cbcopy ; i++) {
                *pldest++ = *plsrc++;
            };
            TKTCYield();
        };
    };
    if (cbodd) {
        for (pbsrc = (uchar_t *) plsrc, pbdest = (uchar_t *) pldest, i = 0 ;
            i < cbodd ; i++)
            *pbdest++ = *pbsrc++;
    };
    return;
}

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

/***LP  vmQueryObject - Query object permissions and attributes
 *
 *      This function is called by VMQueryMem to return object permissions
 *      and attributes. Object sempahore must be obtained prior to calling
 *      this function.
 *
 *      WARNING: This procedure is in swappable code!
 *
 *      int     vmQueryObject(VMOI *poi, ulong_t p, ulong_t *pcb, ulong_t *pfl)
 *
 *      ENTRY           pod             - Pointer to object information
 *                      laddr           - Region's virtual address
 *                                        (page aligned)
 *                      pcb             - Pointer to region's size in bytes
 *                      pfl             - Pointer to entry and return flags
 *      RETURN          int             - Result code (NO_ERROR if no error)
 *
 */
int     vmQueryObject(pod, laddr, pcb, pfl)
register VMOD           *pod;           /* Pointer to Object Data */
ulong_t                 laddr;          /* Linear address */
ulong_t                 *pcb;           /* Size in bytes */
ulong_t                 *pfl;           /* Pointer to flags */
{
  ulong_t               fl;
  ulong_t               lpg = laddr >> PAGESHIFT;
  ulong_t               cpg = (*pcb) >> PAGESHIFT;
  int                   rc;

  TKMayBlock();

  /*
   *  Truncate the range to fit entirely within the object
   *  if necessary.
   */

  if(laddr + *pcb > pod->od_laddr + VMSizeOd(*pod) || laddr + *pcb < laddr)
    {
      *pcb = pod->od_laddr + VMSizeOd(*pod) - laddr;
    }

  cpg = *pcb >> PAGESHIFT;

  /*
   *  Call the Page Manager to query either the page state or the
   *  access permissions.
   */

  if(*pfl & VMQ_STATE)
    {
      rc = PGQueryState(lpg, pod->od_pPTDA, SSToDS(&cpg), pfl);
    }

  else if((rc = PGQueryAccess(lpg, pod->od_pPTDA, SSToDS(&cpg), pfl)) ==
               NO_ERROR)
    {
      /*
       *  Get the object's initial permissions.
       */

      fl = VMGetObPermOd(pod);

#     if (PG_R != PQ_R) || (PG_W != PQ_W) || (PG_X != PQ_X) || \
               (PG_GUARD != PQ_GUARD)
#       error "flags conflict"
#     endif

      /*
       *  If the pages are committed, the page manager returns the true
       *  hardware access permissions, and we must limit the read and
       *  execute access permissions to the initial requested permissions.
       *  (Note that we don't strip read if write is returned.)
       *  If the pages are not committed, return the initial requested
       *  access permissions (also known as the default permissions).
       */

      if(*pfl & PQ_COMMIT)
        {
          *pfl &= (fl & (PQ_R|PQ_X)) | ~(PQ_R|PQ_X) |
                        (*pfl & PQ_W ? PQ_R : 0);
        }
      else
        {
          *pfl |= fl & (PQ_R|PQ_W|PQ_X|PQ_GUARD);
        }

      if(laddr == pod->od_laddr)
        {
          *pfl |= VMQ_BASE;
        }

      if(VMSysArena(laddr))
        {
          *pfl |= VMQ_ARENASYS;
        }
      else if(VMShrArena(laddr))
        {
          *pfl |= VMQ_ARENASHR;
        }
      else if(VMPrvArena(laddr, pod->od_pPTDA))
        {
          *pfl |= VMQ_ARENAPRV;
        }

      *pfl |= VMQ_RESERVED;
      /*
       *  Set the return size.
       */

      if(rc == NO_ERROR)
        {
          *pcb = cpg << PAGESHIFT;
        }
    }

  return(rc);
}

#pragma code_seg()



