#define SCCSID  "@(#)vmob.c     6.8 92/01/09"

/*
 *       IBM/Microsoft Confidential
 *
 *       Copyright (c) IBM Corporation 1987, 1991
 *       Copyright (c) Microsoft Corporation 1987, 1989
 *
 *       All Rights Reserved
 *
 *       History of Changes:
 *       147968     RJM - Removed FSD and DD owner limits.
 */

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

#define INCL_DOSP
#include <os2p.h>

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

#include                <mi.h>
#include                <dossymc.h>
#include                <syiexp.h>
#include                <sigdef.h>
#include                <except.h>
#include                <pgexport.h>
#include                <vmexport.h>
#include                <vmlocal.h>
#include                <vmowner.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                <ldrxport.h>


/***LD+ aulvmAlignMask - table of alignment masks
 *
 *      This table is indexed by the alignment type field
 *      in the allocation request flags.  Three possible
 *      alignments are supported: page, 64K, and 4M.
 */

ulong_t                 aulvmAlignMask[] =
                          {             /* Table of alignment masks */
                            0,
                            0x3FFFFFL >> PAGESHIFT,
                            0xFFFFL >> PAGESHIFT,
                            0L
                          };
/*end*/
#if (VMAC_ALIGNDIR >> VMAC_ALIGNSHIFT) != 1
extern int              Compile_time_Assertion_Failure;
/*      Compile-time Assertion Failure */
#endif
#if (VMAC_ALIGNSEL >> VMAC_ALIGNSHIFT) != 2
extern int              Compile_time_Assertion_Failure;
/*      Compile-time Assertion Failure */
#endif
#if (VMAC_ALIGNPAGE >> VMAC_ALIGNSHIFT) != 3
extern int              Compile_time_Assertion_Failure;
/*      Compile-time Assertion Failure */
#endif


/***LD+ vmSGSFl - Table that converts VMSGS_ flags to PG_ flags in the
 *          case of access invalidation
 */
int     vmSGSFl[] = { -1,                   /* invalid */
                        PG_SKIPFREE|PG_U, /* remove write access */
                        PG_SKIPFREE|PG_W, /* remove user access */
                        PG_SKIPFREE|NULL   /* remove both */
                    };

#define  vmSGSFlMask  (VMSGS_W | VMSGS_U)   /* mask for SGS flags */


/***EP  VMHandleVerify - verify that handle is valid
 *
 *      This function verifies that the given handle is valid.
 *      If it is, then if the handle is a pseudo-handle, then
 *      the address associated with it is returned, else the
 *      pointer to the object record is returned.
 *      WARNING: This function is called without the handle sempahore,
 *      therefore it must remain in resident code.
 *
 *      VMHandleVerify (hob, fpseudo, pp)
 *
 *      ENTRY   hob             - object record handle
 *              fpseudo         - pseudo-handle flag
 *              pp              - place to return pointer
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *      Validate the handle itself, then map the object record
 *      and validate its contents.  Pass back the appropriate
 *      pointer if valid.
 *
 *      CALLS   vmHandleMap, BMPLastBlock
 *
 *      WARNING NONE
 *
 *      EFFECTS NONE
 */

int                     VMHandleVerify(hob,fpseudo,pp)
VMHOB                   hob;            /* Object handle */
int                     fpseudo;        /* Pseudo-handle flag */
void                    **pp;           /* Place to return pointer */
  {
    register VMOB       *pob;           /* Pointer to object record */

    if(hob != HOBNULL)                  /* If not null handle */
      {
        pob = vmHandleMap(hob);         /* Map the handle */
        if(pob <= BMPLastBlock(pobvmOne) &&
                                        /* If record address in range */
          pob->ob_hobowner != HOBNULL &&
                                        /* and if record is allocated */
          pob->ob_hobowner != VMDEADOWNER &&
                                        /* and if object is not dead */
          ((!(pob->ob_fs & OB_PSEUDO) && !fpseudo) ||
                                        /* and if right type of object */
          ((pob->ob_fs & OB_PSEUDO) && fpseudo)))
          {                             /* then the handle is valid */
            if(pp != NULL) *pp = (void *)(fpseudo? pob->ob_va: pob);
                                        /* Pass pointer if location given */
            return(NO_ERROR);           /* Return successfully */
          }
      }
    return(ERROR_INVALID_HANDLE);       /* Bad handle */
  }


/***EP  VMHandleMap - map an object handle to its object record
 *
 *      This function maps an object handle to its object record.
 *      It does not block.  It is an internal error to pass a
 *      pseudo-handle to this function.
 *      WARNING: This function must remain in resident code.
 *
 *      VMHandleMap (hob)
 *
 *      ENTRY   hob             - object record handle
 *      EXIT    VMOB *          - pointer to object record
 *
 *      CALLS   vmHandleMap
 *
 *      WARNING VMHandleVerify is the preferred interface.
 *
 *      EFFECTS NONE
 */

VMOB                    *VMHandleMap(hob)
VMHOB                   hob;            /* Object Record handle */
  {
    vmAssert(VMHandleVerify(hob,0,NULL) == NO_ERROR);
                                        /* Validate handle */
    return(vmHandleMap(hob));           /* Return pointer to record */
  }



/***EP  VMGetSem - Get ownership of Object semaphore
 *
 *      VMGetSem performs combinations of the following functions on a
 *      Memory Object or Pseudo-Object:
 *      WARNING: This function must be in resident code.
 *
 *      (1) Obtain ownership of the semaphore
 *      (2) Query the state of the semaphore
 *      (3) Wait if the semaphore is owned
 *      (4) Wait if the object is locked short-term
 *      (5) Validate an object record handle
 *
 *      Other functions are possible, too, and are described below.
 *      Note that if the calling thread already owns the semaphore,
 *      then the call will return successfully immediately and if the
 *      call requests ownership, then the ownership count for the
 *      semaphore will be incremented.
 *
 *      BIG WARNING:    Since objects may be freed during a yield or block
 *                      care must be taken when using this function.
 *
 *                      *  The object handle MUST BE VALID at time of call AND
 *                         at least one of the following conditions MUST be
 *                         met..
 *
 *                      *  Caller knows that the semaphore is available and
 *                         can be taken without blocking.
 *                      *  VMGS_NOBLOCK is specified.
 *                      *  Caller knows that the object will not be freed at a
 *                         yield within this call.
 *                      *  VMGS_ERRBLOCK has been specified.  This ensures
 *                         that VMGetSem will not try to access the object
 *                         after it has been freed.
 *
 *                      When validity of the object handle is not guaranteed
 *                      after a yield, VMGetSem MUST be imbedded in a loop
 *                      that re-derives the handle from information that will
 *                      not change during a yield, such a s a virtual address
 *                      and context, or a process id.  The VMGetSem call MUST
 *                      specify VMGS_ERRBLOCK and the loop should repeat while
 *                      ERROR_BLOCKED is returned from VMGetSem.
 *
 *      int VMGetSem(VMHOB hob, ulong_t fl, VMOB **ppob)
 *
 *      ENTRY           hob             - Handle of a Memory Object.
 *                      fl              - Flags (optional, see also NOTES
 *                                        on flags below).
 *                                          VMGS_TAKESEM
 *                                          - Take ownership if available
 *                                          VMGS_LLOCKWAIT
 *                                          - Wait if locked long-term
 *                                          VMGS_SLOCKWAIT
 *                                          - Wait if locked short-term.
 *                                            WARNING:  We wait without
 *                                            timeout while there are
 *                                            non-devhlp short-term locks.
 *                                            If there are only short-term
 *                                            devhlp locks we wait only 10
 *                                            seconds.  This is so that a
 *                                            device driver does not lock up
 *                                            resources indefinitely.
 *                                          VMGS_NOBLOCK
 *                                          - Return error immediately if busy.
 *                                          VMGS_ERRBLOCK
 *                                          - Return error after blocking if
 *                                            busy.
 *                                          VMGS_IGNINT
 *                                          - Ignore abnormal wakeup from
 *                                            TKBlock.  This includes an
 *                                            interruption or timeout.
 *                                          VMGS_PSEUDO
 *                                          - Handle is for a pseudo-object.
 *                                          VMGS_VERIFY
 *                                          - Return error if handle invalid
 *                      pp              - Pointer to place to return
 *                                        object record pointer (VMOB **)
 *                                        for regular object or object address
 *                                        for pseudo-object (may be NULL)
 *      RETURN          int             - Result code (NO_ERROR if successful)
 *
 *      NOTES on flags:
 *
 *      The following action flags are mutually exclusive:
 *
 *      VMGS_PSEUDO and VMGS_LLOCKWAIT
 *      VMGS_PSEUDO and VMGS_SLOCKWAIT
 *      VMGS_NOBLOCK and VMGS_ERRBLOCK
 *      VMGS_NOBLOCK and VMGS_IGNINT
 *
 *      All other combinations are valid.
 *
 *      The seven high-order bits of the semaphore count field are used
 *      as the actual count.  The low-order bit is used as the waiting
 *      flag.  Thus, to increment the count, one actually adds two to
 *      the field.
 *
 *      The block i.d. for the semaphore is the 32-bit virtual address
 *      of the object record itself.
 *
 *      The seven high-order bits of the long-term lock count field are
 *      used as the actual count.  The low-order bit is used as the waiting
 *      flag.  Thus, to increment the count, one actually adds two to
 *      the field.
 *
 *      The block i.d. for long-term locks is the 32-bit virtual address
 *      of the object record's long-term lock count field.
 *
 *      The seven high-order bits of the short-term lock count field are
 *      used as the actual count.  The low-order bit is used as the waiting
 *      flag.  Thus, to increment the count, one actually adds two to
 *      the field.
 *
 *      The block i.d. for short-term locks is the 32-bit virtual address
 *      of the object record's short-term lock count field.
 *
 *      CALLS           vmHandleMap, VMHandleVerify, TKSleep
 *
 *      WARNING         ERROR_INTERRUPT will be returned by the function if
 *                      TKBlock returns prematurely and VMGS_IGNINT has not
 *                      been specified.  With strict checks enabled, an
 *                      internal error will be generated if the semaphore
 *                      ownership count overflows.      It is assumed that
 *                      object semaphores can only be taken and released at
 *                      task time.  Since this function resides in resident
 *                      kernel memory (where the kernel is non-preemptive),
 *                      there is no need to test and set the semaphore
 *                      atomically.
 *
 *      EFFECTS         Semaphore and count in appropriate object record may
 *                      be updated.
 */

int                     VMGetSem(hob,fl,pp)
VMHOB                   hob;            /* Handle of object record */
ulong_t                 fl;             /* Flags specifying action to take */
void                    **pp;           /* Place to return pointer */
{
  register VMOB         *pob;           /* Pointer to object record */
  ushort_t              wthdno;         /* Thread number */
  ulong_t               cTimeOut;       /* Timeout value */
  int                   rc;             /* Result code */
  USHORT                cnest;          /* Semaphore Count */

  /*
   *  A pseudo-object does not have locks imposed through its handle.
   *  Verify that either object is not a pseudo-object or that lock waiting
   *  has not speen specified.
   */

  vmAssert(!(fl & VMGS_PSEUDO) || !(fl & (VMGS_LLOCKWAIT | VMGS_SLOCKWAIT)));
  wthdno = pTCBCur->TCBNumber;
  cTimeOut = -1;

  /*
   *  Main loop to take/wait on semaphore
   */

  for(;;)
  {
      /*
       *  For a non-VMSTRICT kernel, verify that the handle is valid only
       *  if verification is requested.  For a VMSTRICT kernel, always
       *  verify the handle; return an error if invalid and verification
       *  was requested; raise an internal error if the handle is invalid
       *  but verification was not requested.
        */

#ifndef VMSTRICT
      if(fl & VMGS_VERIFY)
      {
#endif
          if(VMHandleVerify(hob,fl & VMGS_PSEUDO,NULL) != NO_ERROR)
          {
              vmAssert(fl & VMGS_VERIFY);
              return(ERROR_INVALID_HANDLE);
          }
#ifndef VMSTRICT
      }
#endif

      /*
       *  Point to object record.  If waiting for short-term locks to clear,
       *  examine short-term lock count.
       */

      pob = vmHandleMap(hob);
      if((fl & VMGS_SLOCKWAIT) && pob->ob_cslock != 0)
      {
          /*
           *  Return ERROR_LOCKED if not allowed to block, else set short-term
           *  lock waiting flag.  Note that whenever someone is waiting, the
           *  lock waiting flag must be set, however the converse is not true.
           *  The lock waiting flag only gets cleared when the lock count
           *  goes to zero.
           */

          if(fl & VMGS_NOBLOCK)
          {
              return(ERROR_LOCKED);
          }

          /*
           * B731252 - Internal error if the current thread already owns
           *           the semaphore since the object cannot be unlocked
           *           without owning the semaphore and any unlock would
           *           wait for this thread to free the object semaphore.
          */

          vmAssert(pob->ob_wsemowner != wthdno);

          pob->ob_xflags |= VMOB_SLOCK_WAIT;

          /*
           *  If there are only short-term devhlp locks, limit the time we
           *  will wait to 10 seconds.  We do not trust device drivers to
           *  release devhlp short-term locks.
           */

          if((!(fl & VMGS_IGNINT)) && (pob->ob_cslock != 0))
          {
              cTimeOut = VMOB_CSLOCK_TIMEOUT;
          }

          /*
           *  Wait indefinitely or for 10 secs for locks to clear.  If an
           *  abnormal return, check that it is a signal or a timeout.
           *  Unless masking signals and timeouts, return ERROR_INTERRUPT if
           *  a signal; ERROR_LOCKED if a timeout. If masking signals and
           *  timeouts, retry the operation.
           */

          if((rc = TKSleep(&(pob->ob_cslock),cTimeOut,0,0)) != NO_ERROR)
          {
              vmAssert(rc == ERROR_INTERRUPT || rc == ERROR_TIMEOUT);

              if(!(fl & VMGS_IGNINT))
              {

                  if(rc == ERROR_TIMEOUT)
                  {
                      /*
                       *  If we time out, clear the lockwait flag so no one
                       *  thinks there are waiters when there really aren't.
                       *  In case there is an other waiter, do a TKWakeup
                       *  to get them going.  Any blocker should be able
                       *  to handle a spurious wakeup.
                       */

                      #ifdef VMSTRICT
                        printf("VMOB: Short Term Lock for > 10 secs: hob=%lx\n",hob);
                      #endif

                      pob->ob_xflags &= ~VMOB_SLOCK_WAIT;
                      TKWakeup(&(pob->ob_cslock), 0, NULL);
                      rc = ERROR_LOCKED;
                  }
                  return(rc);
              }
          }

          /*
           *  Normal wakeup or signal when signals being ignored.  Return
           *  ERROR_BLOCKED if so requested.
           */

          if(fl & VMGS_ERRBLOCK)
          {
              return(ERROR_BLOCKED);
          }

          /*
           *  Normal awakening or signals and timeouts masked.  May be spurious
           *  awakening for event with same id or locks may still exist.
           *  Repeat loop to check situation and wait again if necessary.
           */

          continue;
      }

      /*
       *  If waiting for long-term locks to clear,  examine long-term lock
       *  count.  NOTE:  Waiting for long-term locks is exceptional (This path
       *  may not actually be used anywhere).  So much for that thought, LS
       *  uses this path in its server initialization.
       */

      if((fl & VMGS_LLOCKWAIT) && pob->ob_cllock != 0)
      {
///////// vmAssert(0);    remove assertion for LS, driver 174
          /*
           *  Return error if we're not allowed to block, otherwise set
           *  long-term lock waiting flag and wait indefinitely for a clear
           *  event.
           */

          if(fl & VMGS_NOBLOCK)
          {
              return(ERROR_LOCKED);
          }
          pob->ob_xflags |= VMOB_LLOCK_WAIT;

          rc = KSEMQueryMutex((HKSEM)&LdrSem, SSToDS(&cnest)); //D93289/D101249
          if (rc == TRUE && cnest != 0)                        //D93289/D101249
               {                                               //D93289/D101249
               // LDRClearSem();                               //D93289/D101249
               KSEMReleaseMutex( (HKSEM)&LdrSem );             //D110795
               cnest = TRUE;                                   //D93289/D101249
               }                                               //D93289/D101249
          else                                                 //D93289/D101249
               cnest = FALSE;                                  //D93289/D101249

          if((rc = TKSleep(&(pob->ob_cllock),-1L,0,0)) != NO_ERROR)
          {
              /*
               *  Abnormal wakeup.  Must be signal, not timeout, since
               *  we specified an indefinite wait.
               */

              vmAssert(rc == ERROR_INTERRUPT);
              if(!(fl && VMGS_IGNINT))
              {
                  if (cnest == TRUE)                           //D93289/D101249
                      LDRGetSem();                             //D93289/D101249
                  return(ERROR_INTERRUPT);
              }
          }

          if (cnest == TRUE)                                   //D93289/D101249
              LDRGetSem();                                     //D93289/D101249

          /*
           *  Normal wakeup, or signal when signals being ignored.  If
           *  requested, return error indicating that we blocked instead
           *  of trying to take semaphore.  Otherwise try again.
           */

          if(fl & VMGS_ERRBLOCK)
          {
              return(ERROR_BLOCKED);
          }
          continue;
      }

      /*
       *  All relevant locks are clear.  Check if semaphore is clear or
       *  owned by this thread and if so, break to take it.
       */

      if(pob->ob_wsemowner == 0 || pob->ob_wsemowner == wthdno)
      {
          break;
      }

      /*
       *  Semaphore is owned by another thread.  Return error if we cannot
       *  block.
       */

      if(fl & VMGS_NOBLOCK)
      {
          return(ERROR_NOBLOCK);
      }

      /*
       *  Set semaphore waiting flag and wait indefinitely for a clear
       *  event.
       */

      pob->ob_bsemcnt |= VMOB_CBSEM_WAIT;
      TKDeclareInversion (pob->ob_wsemowner);
      if((rc = TKSleep(pob,-1L,0,0)) != NO_ERROR)
      {
          /*
           *  Abnormal wakeup.  Must be signal since indefinite wait specified.
           *  return ERROR_INTERRUPT unless requested to ignore signals.
           */

          vmAssert(rc == ERROR_INTERRUPT);
          if(!(fl & VMGS_IGNINT))
          {
              return(rc);
          }
      }

      /*
       *  Normal wakeup or ignoring interrupts.  Return ERROR_BLOCKED if so
       *  requested.
       */

      if(fl & VMGS_ERRBLOCK)
      {
          return(ERROR_BLOCKED);
      }

      /*
       *  Wakeup may be spurious, or semaphore may still not be
       *  available.  Repeat loop to check.
       */
  }

  /*
   *  The semaphore is available.  Take ownership if requested by storing
   *  the thread id and incrementing the ownership count.  Internal error
   *  if counter overflows.
   */

  if(fl & VMGS_TAKESEM)
  {
      if (pob->ob_wsemowner == 0)
      {
          ++pTCBCur->TCBcBoostLock;     /* TKEnterInversion if first level */
      }

      pob->ob_wsemowner = wthdno;
      pob->ob_bsemcnt += VMOB_CBSEM_INC;
      vmAssert(pob->ob_bsemcnt >= VMOB_CBSEM_INC);

#ifdef VMSTRICT
      ++pTSDCur->TSDcObjSem;            /* Increment count of semaphores held */
#endif
  }

  /*
   *  Return pointer to object record if requested.
   */

  if(pp != NULL)
  {
      *pp = (pob->ob_fs & OB_PSEUDO)? pob->ob_va: pob;
  }
  return(NO_ERROR);
}


/***EP  VMGetPTDAAddr - Return the virtual address of a PTDA
 *
 *      Given a handle to a PTDA, this function returns the
 *      virtual address of the PTDA.  If the null handle is
 *      passed in, then the virtual address of the current
 *      process' PTDA is returned.
 *
 *      VMGetPTDAAddr (hobptda)
 *
 *      ENTRY   hobptda         - handle of PTDA (VMHOB)
 *      RETURN  struct ptda_s * - virtual address of PTDA
 *
 *      This procedure calls VMPseudoHandleMap to obtain
 *      the virtual address of the PTDA.
 *      WARNING: This procedure must remain in resident code.
 *
 *      CALLS   VMPseudoHandleMap
 *
 *      WARNING When assertion checking is enabled, this function
 *              verifies the PTDA signature.
 *
 *      EFFECTS NONE
 */

struct ptda_s           *VMGetPTDAAddr(hobptda)
VMHOB                   hobptda;        /* PTDA handle */
  {
    register struct ptda_s *pptda;         /* PTDA pointer */

    pptda = (hobptda != HOBNULL)? VMPseudoHandleMap(hobptda): pPTDACur;
                                        /* Map handle to PTDA address */
    vmAssert(pptda->ptda_signature[0] == 'T' &&
      pptda->ptda_signature[1] == 'D'); /* Verify signature */
    return(pptda);                      /* Return pointer */
  }


/***EP  VMGetHandle - find object record handle
 *
 *      Given an address and a context, this function finds
 *      the handle for the object record that describes the
 *      object (if any) that contains the specified location
 *      in the specified context.
 *
 *      int VMGetHandle(ulong_t laddr, VMHOB hobPTDA, phob)
 *
 *      ENTRY           laddr           - Linear address
 *                      hobPTDA         - PTDA handle
 *                      phob            - Place to return object record handle
 *      RETURN          int             - Result code (NO_ERROR if successful)
 *
 *      Call VMGetOd to get the object data.  Map the object record to
 *      its BMP32 handle.
 *
 *      CALLS   VMGetOd, vmPobToHob
 *
 *      WARNING This function must not block after calling VMGetOd.
 *              It assumes that while it is active, no other threads are
 *              modifying the structures it is searching.
 *
 *      EFFECTS NONE
 */

int                     VMGetHandle(laddr, hobPTDA, phob)
ulong_t                 laddr;          /* Linear address */
VMHOB                   hobPTDA;        /* PTDA handle */
register VMHOB          *phob;          /* Place to return object handle */
{
  ulong_t               rc;             /* Result code */
  VMOD                  od;             /* Object data */

  od.od_laddr = laddr;
  od.od_pPTDA = VMGetPTDAAddr(hobPTDA);

  if((rc = VMGetOd(NA, SSToDS(&od))) == NO_ERROR)
    {
      TKBeginNoBlock();
      *phob = vmPobToHob(od.od_pob);
      TKEndNoBlock();
      return(NO_ERROR);
    }

  /*
   *  Linear address and context does not belong to an object.  Return error.
   */

  return(rc);
}



/***OP  VMObjectInfo - Return Memory Object information
 *
 *      OBSOLESCENT!  Use VMGetOd
 *
 *      WARNING:  This function is retained only to support existing callers.
 *                All callers should be converted to call VMGetOd
 *
 *      Given a memory object's virtual address and context, or handle
 *      and context, this procedure returns useful information about the object.
 *
 *      VMObjectInfo (p, hobptda, poi)
 *
 *      ENTRY   p               - virtual address.  VM_NO_ADDR can be
 *                                specified if poi->oi_hob contains an
 *                                object handle
 *              hobptda         - PTDA handle
 *              poi             - Memory Object information buffer
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *      Call VMGetHandle to get the handle associated with
 *      the specified address and context.  Map the handle to its
 *      object record.  Search the arena record list for a record
 *      containing the specified address.  If there is a context
 *      list, search it to find a matching PTDA handle.  Fill in
 *      the return information buffer.
 *
 *      CALLS   VMGetObjectInfo
 *
 *      WARNING This function must not block.
 *
 *      EFFECTS Fills in specified buffer.
 */

//int                     VMObjectInfo(p,hobptda,poi)
//ulong_t                 p;              /* Virtual address or -1 */
//VMHOB                   hobptda;        /* PTDA handle */
//VMOI                    *poi;           /* Return buffer */
//  {
//    /*
//     *  Fill in the context and address in the object info buffer
//     *  and call VMGetObjectInfo function to do the work.
//     */
//
//    poi->oi_va = p;
//    poi->oi_hobptda = hobptda;
//    return(VMGetObjectInfo(NA, poi));
//  }



/***OP  VMGetObjectInfo - Return Memory Object information
 *
 *      OBSOLESCENT!  Use VMGetOd
 *
 *      WARNING:  This function is retained only to support existing callers.
 *                All callers should be converted to call VMGetOd
 *
 *      Given a memory object's virtual address, object [or, later, an arena
 *      record handle], and context, this function returns useful information
 *      about the object.
 *
 *      WARNING: This function must remain in resident code.  Function
 *               must not block except when semaphore taking/waiting
 *               etc. flags are specified.
 *
 *      VMGetObjectInfo (fl, poi)
 *
 *      ENTRY   fl              - flags
 *                                  VMOI_VA - Virtual adddress specified
 *                                  VMOI_HOB - Object handle specified
 *                                  VMOI_HAR - Arena record handle specified
 *                                  VMGS_* - VMGetSem flags
 *              poi             - Memory Object information buffer.  This
 *                                is partially filled in on entry depending
 *                                on the flags specified, as follows:
 *                                  poi->oi_hobptda = PTDA pseudo-handle
 *                                    for non-system objects
 *                                  If VMOI_VA specified:
 *                                    poi->oi_va = base address of object or
 *                                    alias
 *                                  Else if VMOI_HOB specified:
 *                                    poi->oi_hob = object handle
 *                                  Else if VMOI_HAR specified:
 *                                    poi->oi_har = arena record handle
 *
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *      EFFECTS                 The object info buffer is filled in
 *
 *      Obtain the object handle from the object info buffer, (if VMOI_HOB)
 *      or by using VMGetHandle and the specified address and context
 *      in the object info buffer (if VMOI_VA).  Map the handle to its
 *      object record.  Search the arena record list for a record
 *      containing the specified address.  If there is a context
 *      list, search it to find a matching PTDA handle.  Fill in
 *      the return information buffer.
 *      For best performance, this procedure does minimum amount of
 *      calls, doing most of its work in line.
 *
 *      CALLS   TKBeginNoBlock, vmHandleMap, vmAliasAr, vmArMap
 *              vmSysArena, VMGetOd, VMGetPTDAAddr, VMOdToOi
 *
 *      EFFECTS Fills in specified buffer.
 */

//int                     VMGetObjectInfo(fl ,poi)
//ulong_t                 fl;             /* flags */
//register VMOI           *poi;           /* Return buffer */
//{
//  ulong_t               laddr;          /* Linear address */
//  register VMAR         *par;           /* Pointer to arena record */
//  VMOD                  od;             /* Object data */
//  VMHAR                 har;            /* Arena record handle */
//  int                   rc;             /* Result code */
//
//  TKBeginNoBlock();
//
//  laddr = poi->oi_va;
//  if(fl & (VMOI_HOB | VMOI_HAR))
//    {
//      if(fl & VMOI_HOB)
//        {
//          od.od_pob = vmHandleMap(poi->oi_hob);
//          vmAssert(od.od_pob->ob_har != HARNULL)
//          par = vmArMap((od.od_pob)->ob_har);
//          while(vmAliasAr(par))
//            {
//              vmAssert((har = par->ar_harlink) != HARNULL);
//              par = vmArMap(har);
//            }
//          laddr = par->ar_ipg << PAGESHIFT;
//        }
//      else
//        {
//          od.od_par = vmArMap(poi->oi_har);
//          laddr = od.od_par->ar_ipg << PAGESHIFT;
//        }
//    }
//  od.od_laddr = laddr;
//
//  if(VMSysArena(laddr))
//    {
//      poi->oi_hobptda = HOBNULL; /* PTR B722347; Changed from a '=='! Edd */
//    }
//
//  if(poi->oi_hobptda == HOBNULL)
//    {
//      poi->oi_hobptda = pPTDACur->ptda_handle;
//    }
//  od.od_pPTDA = VMGetPTDAAddr(poi->oi_hobptda);
//
//  TKEndNoBlock();
//  if((rc = VMGetOd(fl, SSToDS(&od))) == NO_ERROR)
//    {
//      VMOdToOi(SSToDS(&od), poi);       /* Fill in object info */
//    }
//
//  return(rc);
//}


/***EP  VMQueryMaxAlloc - return the size of the largest allocatable object
 *
 *      This function returns the size of the largest swappable object
 *      in bytes that can be allocated successfully in the current process'
 *      private arena.  Since preemption may occur between the time
 *      this function returns and the time a call is made to allocate
 *      an object, there is no guarantee that an allocation request
 *      for the size returned by this function will succeed.
 *
 *      WARNING  This function may block and so the value returned
 *               may be out of date
 *      WARNING: This function walks arena records without taking object
 *      semaphores. Therefore it must remain in resident code.
 *
 *      VMQueryMaxAlloc (pcb)
 *
 *      ENTRY   pcb             - place to return size (dword far *)
 *
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *      PSEUDO CODE
 *
 *      Walk the private tiled and untiled arenas searching for
 *        the largest free gap in virtual address space.  Count
 *        also the gap between the end of the private tiled arena's
 *        highest record and the and the start of the shared arena
 *      Call the Page Manager to return the largest amount of
 *        swappable memory that can be allocated
 *      Return the minimum of the above two values
 */

int VMQueryMaxAlloc(pcb)
ulong_t *pcb;                           /* Count of number of bytes */
  {
    short i;                            /* Tiled/untiled index */
    register VMAR *parprev;             /* Pointer to current arena record */
    register VMAR *parnext;             /* Pointer to next arena record */
    register VMAR *parSen;              /* Pointer to arena sentinel */
    int rc;                             /* result code */
    ulong_t cpgGap;                     /* Gap between arena records */
    ulong_t cpgMaxVirtual = 0;          /* Size of largest contiguous range
                                         * of virtual addresss that can be
                                         * allocated in the private (tiled,
                                         * untiled arena
                                         */
    ulong_t cpgMaxStorage;              /* Number of pages that can be
                                         * committed
                                         */

    for(i=0; i<2; i++)
      {
        /*
         * Point to sentinel for private (tiled,untiled) arena
         */

        parSen = pPTDACur->ptda_ah.ah_parSen;
                                        /* Map arena sentinel */
        parnext = vmArMap(parSen->ar_harnext);
                                        /* Map 1st arena record after
                                                sentinel */

        if(parnext == parSen)           /* If private tiled,untiled
                                                arena is empty, skip */
          continue;

        /*
         * First gap is the distance from the start of the private
         * (tiled, untiled) arena and the first record in the arena
         */

        if((cpgGap = parnext->ar_ipg - parSen->ar_ipg) > cpgMaxVirtual)
          cpgMaxVirtual = cpgGap;

        parprev = parnext;              /* Start at record after sentinel */

        /*
         * Scan all arena records in the private(tiled,untiled)
         * arenas and find the largest gap between adjacent records
         */

        while((parnext = vmArMap(parprev->ar_harnext)) != parSen)
          {
            if((cpgGap = parnext->ar_ipg - parprev->ar_ipg - parprev->ar_cpg) >
              cpgMaxVirtual)
              cpgMaxVirtual = cpgGap;
            parprev = parnext;          /* Next record becomes previous */
          }

        /*
         *  Finally, check the gap between the end of the address space
         *  mapped by the last record in the private (tiled,untiled) arena and
         *  the end of the no-man's land between the private (tiled,untiled)
         *  and shared (tiled,untiled) arenas.
         */

        parSen = ahvmShr.ah_parSen;
        if((cpgGap = parSen->ar_ipg -
                    parprev->ar_ipg -
                    parprev->ar_cpg) > cpgMaxVirtual)
          cpgMaxVirtual = cpgGap;

#ifdef OS2V13
        break;
#endif
      }

    /*
     * Call the Page Manager to query the max amount of storage that can
     * be committed
     */

    if((rc = PGAllocSizeQuery(SSToDS(&cpgMaxStorage))) == NO_ERROR)
      {
        if(cpgMaxVirtual < cpgMaxStorage)
          *pcb = cpgMaxVirtual << PAGESHIFT;
        else
          *pcb = cpgMaxStorage << PAGESHIFT;
      }

    return(rc);
  }


/***EP  VMSGSControl  - Add the address range to SGS list
 *
 *      VMSGSControl is called to add the  given memory/mapping
 *      to the SGS list. This mapping is removed
 *      from the list by VMFree when it is being freed.
 *
 *      ENTRY   fl      -   VMSGS_W, VMSGS_U, VMSGS_DBG
 *              laddr   - Address of memory/mapping
 *      EXIT    int     - return code (NO_ERROR if no error)
 *
 *      int  VMSGSControl(ushort fs, ulong laddr)
 *
 *      PseudoCode
 *
 *      Find the HAR corresponding to laddr
 *      Call vmSGSAdd
 *      return
 */
int     VMSGSControl(fs, laddr)
ushort_t    fs;
ulong_t     laddr;
{
    int     rc;
    VMOD    od;

    vmAssert (VMPrvArena(laddr, pPTDACur));   /* Must be in the private arena */
    od.od_laddr = laddr;
    od.od_pPTDA = pPTDACur;
    if ((rc = VMGetOd(NA, SSToDS(&od))) == NO_ERROR) {
        rc = vmSGSAdd(vmParToHar(od.od_par), pPTDACur, fs);
    }
    return(rc);
}


/***EP  VMSGSValidate - Walk SGS Control list to (in)validate page tables.
 *
 *      VMSGSValidate walks the screen group switch control list of the
 *      given ptda to mark the corresponding page tables read/write
 *      or read only.
 *      WARNING: This function is a worker function to TKScanTasks
 *      BUGBUG: We should be using a version of TKScanTasks that can block.
 *
 *      ENTRY   pptda   - Pointer to ptda
 *              pfl     - pflags
 *                          (VMSGS_ADD|VMSGS_REMOVE, [VMSGS_DBG])
 *      RETURN  int     - error code (NO_ERROR if no error)
 *
 *      int VMSGSValidate(struct ptda_s *pptda, ulong_t *pfs)
 *
 *      PseudoCode:
 *
 *      For every entry in the SGSList
 *          Find corresponding ipg range from Arena Record
 *          If (fits the desired type (debug/nondebug))
 *              Call PGSetAccess to mark page tables
 *              Call SELSetAccess to mark LDT
 *              If error, InternalError
 *      return
 */
int VMSGSValidate(pptda, pfl)
struct ptda_s   *pptda;                 /* Pointer to ptda variable */
ulong_t         *pfl;                   /* Enable/disable flags */
{
    register struct vmSGS_s *psgs = pptda->ptda_pSGSList;
                                        /* Pointer to SGS stucture */
    register VMAR    *par;              /* Pointer to AR */
    ushort_t fs = (ushort_t) (*pfl & VMSGS_DBG); /* Desired map type */
    int      fl = (*pfl & VMSGS_ADD) ? PG_U|PG_W|PG_SKIPFREE : PG_SKIPFREE;
                                                /* Desired access type */

    while (psgs != NULL) {              /* List terminates with NULL pointer */
        par = vmArMap(psgs->sgs_har);     /* Find ptr to AR */
        vmAssert(pptda->ptda_handle == par->ar_hobptda);

        if(!fs || (psgs->sgs_fs & VMSGS_DBG)) {
            /* If this entry fits the desired debug/nondebug type */

            /* For VMSGS_ADD, we always pass PG_U and PG_W flags both,
             * for VMSGS_REMOVE, we pass PG_U if VMSGS_W, PG_W if VMSGS_U
             * in order to turn off the  other flag */

            if (fl != PG_SKIPFREE) {
                /* If it is an add request, mark page tables first */
                PGSetAccess(par->ar_ipg, pptda, par->ar_cpg, fl);
                SELSetAccess(par->ar_ipg, pptda, par->ar_cpg, VMSGS_ADD);
            }
            else    {
                /* remove request, mark LDTs first */
                SELSetAccess(par->ar_ipg, pptda, par->ar_cpg, VMSGS_REMOVE);
                PGSetAccess(par->ar_ipg, pptda, par->ar_cpg,
                    vmSGSFl[(psgs->sgs_fs & vmSGSFlMask)]);
            };
        };

        /* Get next item on list */
        psgs = psgs->sgs_psgsnext;
    };
    return(NO_ERROR);
}


/***LP  vmSGSAdd - Register given memory under Screen Group Switch control.
 *
 *      vmSGSAdd adds the given har to the list of hars under screen group
 *      switch control list. Turn AR_SGS flag on.
 *
 *      ENTRY   har     - Handle to arena record to add to SGS list.
 *              pptda   - Pointer to ptda, har belongs to.
 *              fs      - Flags (WRITE, USER, DBG)
 *      RETURN  int     - error code (NO_ERROR if no error)
 *
 *      int     vmSGSAdd(VMHAR har, struct ptda_s *pptda, ushort_t fs)
 *
 */
int         vmSGSAdd(har, pptda, fs)
VMHAR       har;                        /* har to register under SGS control */
struct  ptda_s *pptda;                  /* pPTDA that har belongs to */
ushort_t    fs;                         /* Flags */
{
    int     rc;                     /* return code */
    struct  vmSGS_s *psgs;          /* Pointer to SGS structure allocated */
    VMAR    *par;

    par = vmArMap(har);
    /* Don't use VMPrvArenaPg, because it may belong to a different ptda */
    vmAssert((!VMSysArenaPg(par->ar_ipg))  && (!VMShrArenaPg(par->ar_ipg)));
    vmAssert(par->ar_hobptda == pptda->ptda_handle);

    if ((rc = VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(*psgs), VMSGSOWNER, NA,
            SSToDS(&psgs))) == NO_ERROR) {
        /* Set up and link SGS record */
        vmAssert((fs & ~(VMSGS_W | VMSGS_U | VMSGS_DBG)) == NULL);
        par->ar_f |= AR_SGS;        /* Turn on SGS flag */
        psgs->sgs_fs = fs;
        psgs->sgs_har = har;
        psgs->sgs_psgsnext = pptda->ptda_pSGSList;
        pptda->ptda_pSGSList = psgs;
    }
    return(rc);
}


/***LP  vmSGSRemove - Remove given memory from Screen Group Switch control list.
 *
 *      vmSGSRemove removes the given har from the list of hars under screen
 *      group switch control.
 *      WARNING: This function walks the screen group switch records that are
 *      not protected by a sempahore. Therefore it must remain in resident
 *      code.
 *
 *      ENTRY   har     - Handle to arena record.
 *              pptda   - Pointer to ptda, har belongs to.
 *      RETURN  int     - error code (NO_ERROR if no error)
 *
 *      void    vmSGSRemove(VMHAR har, struct ptda_s *pptda)
 *
 */
void        vmSGSRemove(har, pptda)
VMHAR       har;                        /* har to register under SGS control */
struct  ptda_s *pptda;                  /* pPTDA that har belongs to */
{
    register struct  vmSGS_s *psgs = pptda->ptda_pSGSList;
    struct  vmSGS_s *psgsprev = NULL;
#ifdef  VMSTRICT
    VMAR    *par;
#endif

#ifdef VMSTRICT
    par = vmArMap(har);
    /* Don't use VMPrvArenaPg, because it may belong to a different ptda */
    vmAssert(!VMSysArenaPg(par->ar_ipg)  && !VMShrArenaPg(par->ar_ipg));
    vmAssert(par->ar_hobptda == pptda->ptda_handle);
#endif

    /* Have to find the back link to remove the record */

    while (psgs != NULL) {
        if (psgs->sgs_har == har) break;    /* break if found */
        psgsprev = psgs;                    /* Update back link */
        psgs = psgs->sgs_psgsnext;          /* Get next record */
    }
    vmAssert(psgs != NULL);                 /* Panic if couldn't find */

    /* Record found, now remove it from the link list */
    if (psgsprev == NULL)                   /* first on the list ? */
        pptda->ptda_pSGSList = psgs->sgs_psgsnext;
    else
        psgsprev->sgs_psgsnext = psgs->sgs_psgsnext;

    VMFreeKHB(VM_HKH_PUB_RESRW, (void *) psgs);
                    /* free the record */
}


/***LP  vmIsBusyArBMP - BMP busy function for arena records.
 *
 *      This function is called by BMP to check if an arena record is
 *      busy.  Given an arena record pointer, return true if the specified
 *      arena record is busy.
 *
 *      int vmIsBusyArBMP(VMAR *par)
 *
 *      ENTRY           par             - Arena record pointer
 *      RETURN          int             - TRUE if busy, else FALSE
 *
 *      CALLS           vmIsBusyAr macro
 *
 *      WARNINGS        NONE
 *
 *      EFFECTS         NONE
 */

int vmIsBusyArBMP(par)
VMAR                    *par;           /* Pointer to arena record */
{
  return(vmIsBusyAr(par));
}


/***LP  vmIsBusyObBMP - BMP busy function for arena records.
 *
 *      This function is called by BMP to check if an object record is
 *      busy.  Given an object record pointer, return true if the specified
 *      object record is busy.
 *
 *      vmIsBusyObBMP(pob)
 *
 *      ENTRY           pob             - Object record pointer
 *      RETURN          int             - TRUE if busy, else FALSE
 *
 *      CALLS           vmIsBusyOb macro
 *
 *      WARNINGS        NONE
 *
 *      EFFECTS         NONE
 */

int vmIsBusyObBMP(pob)
VMOB                    *pob;           /* Pointer to object record */
{
  return(vmIsBusyOb(pob));
}

/***LP  vmIsBusyCoBMP - BMP busy function for context records.
 *
 *      This function is called by BMP to check if a context record is
 *      busy.  Given a context record pointer, return true if the specified
 *      context record is busy.
 *
 *      int vmIsBusyCoBMP(VMCO *pco)
 *
 *      ENTRY           pco             - Context record pointer
 *      RETURN          int             - TRUE if busy, else FALSE
 *
 *      CALLS           vmIsBusyCo macro
 *
 *      WARNINGS        NONE
 *
 *      EFFECTS         NONE
 */

int vmIsBusyCoBMP(pco)
VMCO                    *pco;           /* Pointer to context record */
{
  return(vmIsBusyCo(pco));
}


/***EP  VMGetOwner - Get owner of selector-mapped memory object.
 *
 *      VMGetOwner returns the owner of a selector mapped piece of
 *      memory.
 *
 *      ENTRY   sel     the selector
 *              off     offset
 *      RETURN  VMHOB   hob owner -or-
 *                      HOBNULL if owner couldn't be determined
 */

VMHOB VMGetOwner(SEL sel, ulong_t off)
{
    VMKHBI      khbi;
    VMAR        *par;
    VMOB        *pob;
    ushort_t    modtype;
    VMOD        od;

    khbi.khbi_sel = sel;
    if ((sel & SEL_LDTBIT) == 0 &&
        SELGetKHBI(0, SSToDS(&khbi)) == NO_ERROR) {
        return(khbi.khbi_hobOwner);
    }
    od.od_laddr = SELConvertToLaddr(sel) + off;
    od.od_pPTDA = pPTDACur;
    if (VMGetOd(NA, SSToDS(&od)) != NO_ERROR) {
        return(HOBNULL);
    }
    par = od.od_par;
    pob = od.od_pob;
// d147968
//    if (!InitCodeInvalid &&
//        LDRGetModuleType(pob->ob_hobmte, SSToDS(&modtype)) == NO_ERROR) {
//        if (modtype & LDRDEVICE) {
//            return(LDRDDOwner - (ushort_t)1);
//        }
//        if (modtype & LDRFSD) {
//            return(LDRFSDOwner - (ushort_t)1);
//        }
//    }
    return((pob->ob_hobmte != HOBNULL)? pob->ob_hobmte : pob->ob_hobowner);
}


/***EP  VMClearSem - Release a Memory Object's semaphore
 *
 *      This procedure releases ownership of the semaphore for
 *      the Memory Object whose handle is given.  Note that
 *      since ownership may be nested, this procedure does not
 *      actually release the semaphore until the ownership count
 *      goes to zero.  At this point, any threads waiting on the
 *      semaphore will be awakened.
 *
 *      WARNING:  This function must remain in resident code
 *
 *      void VMClearSem(VMHOB hob)
 *
 *      ENTRY           hob             - Memory Object Handle
 *      RETURN          void
 *
 *      This procedure maps the handle to an object record,
 *      decrements the ownership count by two, and if the result
 *      is less than two, it clears the owner field and checks
 *      to see if the ownership count is one.  If the count is
 *      one, then there are threads waiting for the semaphore,
 *      so the count is zeroed, and TKRun is called using the
 *      linear address of the object record as the event i.d.
 *
 *      Unlike other functions, VMClearSem may be called on
 *      dead objects in order to signal waiting threads that
 *      the given object is dead.
 *
 *      CALLS           vmHandleMap, TKWakeup, BMPLastBlock
 *
 *      WARNING         When strict checks are enabled, an internal error
 *                      results if the ownership count underflows.
 *
 *      EFFECTS         Semaphore and count in appropriate object record.
 */

void                    VMClearSem(hob)
VMHOB                   hob;            /* Object's handle */
{
  register VMOB         *pob;           /* Pointer to object record */

  /*
   *  Verify that a non-null object handle has been passed and map it
   *  to an object record pointer.  Check that the resulting pointer
   *  lies within the used portion of the BMP32-managed Object Record Table.
   */

  vmAssert(hob != HOBNULL);
  pob = vmHandleMap(hob);
  vmAssert(pob <= BMPLastBlock(pobvmOne));
  vmAssert(vmIsBusyOb(pob));

  /*
   *  Verify that the object's semaphore is owned by the current thread and
   *  validate the semaphore ownership count.
   */

  vmAssert((ushort_t) pTCBCur->TCBNumber == pob->ob_wsemowner);
  vmAssert(pob->ob_bsemcnt >= VMOB_CBSEM_INC);

  /*
   *  Decrement the semaphore ownership count.  If the count goes to zero,
   *  clear the semaphore.
   */

#ifdef VMSTRICT
  --pTSDCur->TSDcObjSem;                /* Decrement count of semaphores held */
#endif

  if((ushort_t) pTCBCur->TCBNumber == pob->ob_wsemowner)
  {
      if((pob->ob_bsemcnt -= VMOB_CBSEM_INC) < VMOB_CBSEM_INC)
      {
          TKResExitInversion();
          pob->ob_wsemowner = 0;

          /*
           *  If there are threads waiting on the semaphore, clear the waiting
           *  flag and waken them.
           */

          if(pob->ob_bsemcnt & VMOB_CBSEM_WAIT)
          {
              pob->ob_bsemcnt = 0;
              TKWakeup(pob, NA, NA);
          }
      }
  }
}

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

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


/***EP  VMCreatePseudoHandle - create a pseudo-handle and set mapping
 *
 *      This procedure allocates an object record for use
 *      as a pseudo-object and stores the specified address
 *      in it.
 *      WARNING: This procedure is in swappable code
 *
 *      VMCreatePseudoHandle (p, hobowner, phob)
 *
 *      ENTRY   p               - pointer to store in object record
 *              hobowner        - owner i.d.
 *              phob            - place to return object record handle
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *      Allocate an object record.  Set its owner i.d.
 *      Store the given pointer in it.  Clear the semaphore fields.
 *      Return the handle of the object record.
 *
 *      CALLS   vmObAlloc, vmPobToHob
 *
 *      WARNING This procedure may block.
 *
 *      EFFECTS Allocates and initializes an object record.
 */

int                     VMCreatePseudoHandle(p,hobowner,phob)
void                    *p;             /* Pointer */
VMHOB                   hobowner;       /* Owner i.d. */
VMHOB                   *phob;          /* Place to return record handle */
{
    VMOB                *pob;           /* Pointer to object record */
    int                 rc;             /* Return code */

    TKMayBlock();
    vmAssert(hobowner != HOBNULL);      /* Must have valid owner i.d. */
    if((rc = vmObAlloc(SSToDS(&pob))) == NO_ERROR)
      {                                 /* If allocation successful */
        pob->ob_hobowner = hobowner;    /* Set owner i.d. */
        pob->ob_fs = OB_PSEUDO;         /* Mark as pseudo-object */
        pob->ob_va = p;                 /* Store the pointer */
        pob->ob_wsemowner = 0;          /* Clear the semaphore */
        pob->ob_bsemcnt = 0;
        *phob = vmPobToHob(pob);        /* Return the handle */
      }
    return(rc);                         /* Return code */
}


/**************************************************************************
 *                                                                        *
 *  WARNING: Anything added here will be in swappable code!!!!            *
 *                                                                        *
 **************************************************************************/
#pragma code_seg()
