#define SCCSID  "@(#)selmgrc.c  6.14 92/03/26"

/*       IBM/Microsoft Confidential
 *
 *       Copyright (c) IBM Corporation 1987, 1989
 *       Copyright (c) Microsoft Corporation 1987, 1989
 *
 *       All Rights Reserved
 *
 *  09/18/91  Khoa Dang Huynh
 *       CP20 DCR 1486  Provide selector packing capability.
 *  09/18/92  William Hsieh
 *            53382  IPE Panic in selremdiscrec. Free rmp records twice.
 *  09/28/92  Bill Madden  R206 FEA 53758
 *  1.5       Added SelDestroyVdmLDT and SelSwitchVdmLDT.
 *  08/23/93  Change Team - Defect 73188
 *            Changed SelSwitchVdmLDT to accept additional paramater and update
 *            bitmap of valid LDT page before switching LDT's
 */


#define INCL_DOSERRORS
#include <bseerr.h>
#include <os2defp.h>

#define INCL_TYPES
#include <basedef.h>
#include <bsedosp.h>

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

#include                <mi.h>
#include                <dossymc.h>
#include                <sigdef.h>
#include                <except.h>
#include                <seldesc.h>
#include                <vmowner.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                <sellocal.h>
#include                <proc.h>
#include                <pglocal.h>
#include                <mmph.h>

#define  SRL_RESERVE (VMAC_ALIGNSEL)
#define  SELAL_DESCMASK (SELAL_UVIRT | SELAL_GRAN4K | SELAL_32BIT | BYTEMASK)
#define  SEL_VMLDT (VMAC_LOCANY | VMAC_ALIGNPAGE | VMAC_ARENASYS)

#define  SEL_PGLDT1 (PG_DISCARDABLE | PG_ZEROFILL | PG_W | PG_R)
#define  SEL_PGLDT2 (PG_SWAPPABLE | PG_ZEROFILL | PG_W | PG_R)
// GSR start
// #define  SEL_LDTPGMAPINIT  (0x0001 | ( 1 << SEL_INFOSEGPG))
// #define  SEL_LDTVDMPGMAPINIT (0x0001)
// lotus #define  SEL_LDTPGMAPINIT  (0x3c01)
// 152906 #define  SEL_LDTPGMAPINIT  (0x3001)
// the last 4 pages of the LDT are mapped with the last 4 bits of the map
// (12-15) not bits 10-13.
#define  SEL_LDTPGMAPINIT  (0xf001)
// GSR end
#define  RE_FLCS   ( PG_R | PG_X | PG_U)

#define  SEL_LDTVDMNOZERO  0x8000

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

/***EP  SELAlloc - Allocate and set up descriptors
 *
 *      SELAlloc is called by VMAlloc to set up descriptors for a newly
 *      allocated memory object
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY       pob     - pointer to object record
 *                  par     - pointer to arena record
 *                  cb      - size (Original requested size, not AR size)
 *                  flvm    - VM flags (PG_W, PG_R, PG_X, PG_U, and also
 *                            VMAC_ARENASHR, VMAC_ARENA_PRV, VMAC_ARENASYS)
 *                  flsel   - selector allocation flags
 *                              SELAL_SELMAP set if selector requested
 *                              SELAL_32BIT set if 32 bit default format
 *                              SELAL_GDTRPL0 set if GDT RPL0 stamp
 *                              SELAL_ALLOCATED set if specific GDT request
 *                              SELAL_PRELOAD set if preload requested
 *                              SELAL_CODE set if code selector(data if clear)
 *                              SELAL_CONFORM set if conforming code
 *                              SELAL_UVIRT set if uvirt descriptor
 *                              SELAL_W set if writable data
 *                              SELAL_R set if readable code
 *                              SELAL_EXPDN set if expand down descriptor
 *                              SELAL_PRIVSHIFT shift Priv level left by thismuch
 *                  pptda   - Address of ptda
 *                  psel    - Location to write selector
 *      RETURN    int   - error code (NO_ERROR if success)
 *
 *      SELAlloc(VMOB *pob, VMAR *par, ulong cb, ulong flpg,
 *                 ulong flsel, struct ptda_s pptda, SEL *psel)
 *
 *      PseudoCode:
 *
 *      Set laddr = par->ar_ipg << PAGESHIFT ; get linear address
 *      If (not SELMAP)             ; BUGBUG: In v1.3 we set up selector mapping
 *                                  ; even if not requested.
 *          Set up selector flags as ring 0 or 3 data, B bit clear
 *      Else
 *          ; Do consistency checking with PG flags
 *          If (code selector && ( PG_W || not PG_X) )
 *              return (ERROR_INVALID_PARAMETER)
 *          Else if (data selector)
 *              If (PG_X || not PG_R)
 *                  return (ERROR_INVALID_PARAMETER)
 *              Endif
 *          Endif
 *      Endif
 *      If (laddr in system arena)
 *          Call SELAllocGDT
 *      Else                                ; User memory
 *          Convert laddr to selector
 *  ifdef SELSTRICT
 *          If (laddr+cb > 512 Meg), InternalError
 *          If (laddr.low != 0), InternalError
 *  endif
 *          Convert size to no. of selectors
 *          Call selAllocLDTSub
 *      Endif
 *      If (error from LDT/GDT allocation)
 *          return error code
 *      Endif
 *      Call SELSetDescInfo(SELSD_ALL)            ; set up descriptors
 *      If (system area)
 *          Set *par->ar_sel = gdt selector
 *      Endif
 *      Call VMArSetTiled(par)
 *      If (not GDT RPL0)
 *          Call SetRPL
 *      Endif
 *      return (NO_ERROR)
 */
 int    SELAlloc(pob, par, cb, flvm, flsel, pptda, psel)
 VMOB   *pob;           /* pointer to object record */
 VMAR   *par;           /* pointer to Arena Record */
 ulong_t  cb;           /* true size in bytes */
 ulong_t  flvm;         /* VM flags */
 ulong_t  flsel;        /* Selector flags */
 struct   ptda_s *pptda;    /* Address of ptda */
 SEL    *psel;          /* Location to return the selector value */
 {
    int         rc;             /* return code */
    ulong_t     laddr = par->ar_ipg << PAGESHIFT;       /* linear address */
    SEL         sel = 0;

    if (!(flsel & SELAL_SELMAP)) {
                        /* selector mapping was not requested */
        flsel = D_DATA0;
        if (flvm & PG_U) flsel |= D_DPL3;       /* user privilege */
    }
    selAssert (((VMAC_ARENASHR | VMAC_ARENAPRV) & VMAC_ARENASYS) == 0);
    /*
     * Allocate and set up selectors only if requested size != 0. Otherwise
     * set up arena record fields and leave.
     */

    if (cb != 0) {
        if (flvm & (VMAC_ARENASHR | VMAC_ARENAPRV)) {  /* LDT space */
            selAssert ((laddr + cb) <= SEL_512MEG);
            selAssert ((laddr & WORDMASK) == 0);
            if ((rc = selAllocLDTSub((sel = LaToSelTiled(laddr)),
                (ushort_t) ((cb + WORDMASK) >> WORDSHIFT), pptda))
                != NO_ERROR) return(rc);
        } else  {       /* allocation in system area */
            if (flsel & SELAL_ALLOCATED) {    /* already allocated? */
                selAssert(*psel != 0);       /* Selector must not be null */
                sel = *psel;                /* If the selector is already
                                             * allocated, the caller provides
                                             * it in the return buffer
                                             */
            } else
                if ((rc = SELAllocGDT(SSToDS(&sel))) != NO_ERROR)
                    return(rc);
        }
        if(!(flsel & SELAL_GDTRPL0)) sel |= (flsel & D_PRIV) >> SELAL_PRIVSHIFT;
        /* selectors allocated, now set them up */
        if (!(flsel & SELAL_PRELOAD)) flsel |= D_PRES;
        SELSetDescAll((flsel & SELAL_BASEALL) ?
                            SELSD_BASEALL | SELSD_ALL : SELSD_ALL,
            (ushort_t) ((flsel & SELAL_DESCMASK) | D_SEG),
            sel, cb, laddr, pptda);
    }
    if ((flvm & VMAC_ARENAMASK) == VMAC_ARENASYS)
        par->ar_sel = sel;                 /* Save GDT sel in arena record */
    VMArSetTiled(par, flsel & SELAL_BASEALL ? AR_SELMAP | AR_SELBASEALL :
        AR_SELMAP);

    *psel = sel;                            /* return selector value */
    return(NO_ERROR);
 }

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

/***EP  SELFree   - Free descriptor(s) associated with freed memory object
 *
 *      SELFree is called by VMFree to free the descriptors belonging to
 *      a memory object to be freed.
 *
 *      ENTRY   pod - Pointer to object descriptor buffer
 *      RETURN  None
 *      WARNING: This procedure is in swappable code
 *
 *      PseudoCode:
 *
 *      SELFree(VMOD *pod)
 *
 *      PseudoCode:
 *
 *      If (freeing a discardable segment)
 *         If last reference to segment
 *            SELRemDiscRec(hob)           ; Remove the disc segs rmp record
 *         Endif
 *      Endif
 *      If (laddr in system area)
 *          Call selFreeGDTSub   ; GP faults if sel in invalid section of GDT
 *      Else                                ; user area
 *          Call selFreeLDTSub(desc, numsels, pptda)
 *      Endif
 *      return
 */
void        SELFree(pod)
register    VMOD *pod;          /* pointer to object descriptor being freed */
{

    selAssert(VMParOd(*pod)->ar_f & AR_SELMAP);

    /*
     * If freeing a discardable segment AND
     * If the segment is not alias AND                         * 53382 *
     * - The segment is private
     * - The segment is Shared, Private Context
     * - The segment is Shared, Shared Context and this is the last reference
     * remove its RMP record (contains the segment soft lock count).
     */

    if((pod->od_pob)->ob_xflags & VMOB_DISC_SEG)
    {
         if( ((pod->od_par)->ar_hal == 0 ) &&                  /* 53382 */
             ((((pod->od_pob)->ob_fs & OB_SHARED) == 0) ||
              (((pod->od_par)->ar_f  & AR_HCO) == 0)    ||
              (VMHconextCo(*vmCoMap(VMHcoAr(*(pod->od_par)))) == HCONULL) ))
//            (vmCoMap((pod->od_par)->ar_hco)->co_hconext == HCONULL) ))
         {
             SELRemDiscRec(vmPobToHob(pod->od_pob));
         }
    }

    if (!(VMTiled(pod->od_laddr)))
    {
        selAssert(((pod->od_par)->ar_sel & SEL_LDTBIT) == 0);
        selFreeGDTSub((pod->od_par)->ar_sel);
    }
    else        /* LDT space */
    {
        selFreeLDTSub(SELLaToSel(pod->od_laddr, pod->od_pPTDA),
                      (ushort_t) ((VMSizeOd(*pod) + WORDMASK) >> WORDSHIFT),
                      pod->od_pPTDA);
    }

    return;
}

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

/***EP  SELQueryFreeAlias - Detrermine whether a cs alias needs to be freed
 *
 *      SELQueryFreeAlias is called by VMFree to determine whether a cs alias
 *      needs to be freed or not. It must be called for every aliased object
 *      in the private arena that is being freed. It must only be called if
 *      it is not Task Termination. It will return ERROR_BUSY if the
 *      cs alias should not be freed. Otherwise it will return NO_ERROR
 *      WARNING: Object semaphore must be obtained
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   poi     - Pointer to object information
 *      RETURN  int     - Return code ERROR_BUSY if object should not be freed
 *
 *      int SELQueryFreeAlias(poi)
 *
 *      PseudoCode
 *
 *      Call VMSelAlias(csalias flag)
 *      If (error)                      ; it is not a cs alias
 *          return(NO_ERROR)
 *      Endif
 *      If (datasel is being freed)
 *          turn off DSVALID flag in alias record
 *      Else if (code selector being freed)
 *          decrement ref  count
 *          if (nonzero), return(ERROR_BUSY)
 *      Endif
 *      return(NO_ERROR)
 */
//int     SELQueryFreeAlias(poi)
//VMOI    *poi;                       /* Pointer to object information */

int     SELQueryFreeAlias(pod)
VMOD    *pod;                       /* Pointer to object information */
{
    VMAL    *pal;
    SEL      sel;

//  if (VMSelAlias(VMMA_CSALIAS, poi->oi_hob, (void **) (SSToDS(&pal)))
//      == NO_ERROR)

    if (VMSelAlias(VMMA_CSALIAS, vmPobToHob(pod->od_pob),
            (void **) (SSToDS(&pal))) == NO_ERROR)
    {
//      sel = LaToSelTiled(poi->oi_va);
        sel = LaToSelTiled(pod->od_laddr);
        /* It is indeed a cs alias */
        if (sel == ((pal->sal_seldata) & (SEL) SEL_RPLCLR))
        {
            /* data selector being freed */
            pal->al_f &= ~SAL_CSDSVALID;
        }
        else if ((SEL)(sel | SEL_RPL2) == pal->sal_selcode)
        {
            /* code selector being freed */
            if ((--(pal->sal_cref))  != 0)
            {
                return(ERROR_BUSY);         /* there are other references */
            }
        }
    }
    return(NO_ERROR);       /* not a cs alias, or data sel, or cs sel with
                             * ref count=0 */
}

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

/***LP  selValidateLDT - Validate region of LDT covering given descriptors
 *
 *      selValidateLDT checks to see if the page(s) covering the descriptor(s)
 *      is committed. If not, it commits the necessary page(s).
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   pptda   - Pointer to ptda
 *              sel     - starting selector
 *              csel    - no of selectors
 *      RETURN  int     - Error code (NO_ERROR if no error)
 *
 *      int     selValidateLDT(struct ptda_s *pptda, SEL sel, ushort_t csel)
 *
 *      PseudoCode
 *
 *      Set ipg = sel >> PAGESHIFT      ; page no first selector corresponds to
 *      set ipglast = (sel + csel * SEL_SHIFT - 1) >> PAGESHIFT
 *          ; page no in the LDT corresponding to the last selector.
 *      for (i= ipg; i <=ipglast; i++)
 *          if (!(selIsValidLDTpg(pptda, i))) {
 *              Call VMGetSem(ldt hob)
 *              for (; i <= ipglast; i++) {
 *                  If (page is not valid)
 *                      Call PGSetTypeLDT
 *                      Turn the corresponding bit on.
 *                  Endif
 *              }
 *              Call VMClearSem
 *          }
 *      return(TRUE)
 */
int
selValidateLDT(struct ptda_s *pptda,    /* pointer to context's ptda */
               SEL sel,                 /* starting selector value */
               ushort_t csel)           /* no of selectors */
{
    register ushort_t i;
    register ushort_t ipglast = (ushort_t) ((sel + ((csel - 1) << SEL_SHIFT))
        >> PAGESHIFT);
    int     rc = NO_ERROR;


    /* Make sure that last selector value in range is still <= 64K */

    selAssert(((sel >> SEL_SHIFT) + csel - 1) <= SEL_MAXDESC);

    for (i = (ushort_t) (sel >> PAGESHIFT) ; i <= ipglast; i++) {
        /* see if corresponding page in the pgmap is valid */
        if (!(selIsValidLDTpg(pptda, i))) {
            selAssert(pptda->ptda_ldthandle);
            if ((rc = VMGetSem(pptda->ptda_ldthandle, VMGS_TAKESEM, NULL))
                != NO_ERROR) return(rc);
            for ( ; i <= ipglast; i++) {
                if (!(selIsValidLDTpg(pptda, i)))
                {
                    rc = PGSetTypeLDT((pptda->ptda_ldtaddr >> PAGESHIFT) + i,
                        pptda->ptda_ldthandle, i, SEL_PGLDT2);
                    if (rc != NO_ERROR)  break;
                    /* Indicate corresponding page is allocated */
                    pptda->ptda_ldtpgmap |= (1 << i);

                   if(pptda->ptda_ProcType == PT_VDM)
                   {
                       SELFreeVdmSels(i << PAGESHIFT,
                      (PAGESIZE / sizeof(struct desctab) | SEL_LDTVDMNOZERO));
                   }
                }
            }
            VMClearSem(pptda->ptda_ldthandle);
        }
        if (rc != NO_ERROR) break;
    }
    return(rc);
}

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

/***LP  selAllocLDTSub - Allocate LDT selector(s)
 *
 *      selAllocLDTSub is called by SELAlloc and SELAllocLDT to allocate
 *      selector's from the given ptda's LDT.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sel     - Base selector value to be allocated
 *              csel    - No of selectors to be allocated
 *              pptda   - Address of ptda
 *      RETURN  int     -  error code (NO_ERROR if no error)
 *
 *      selAllocLDTSub(SEL sel, ushort_t csel, void *pptda)
 *
 *      PseudoCode:
 *
 *      If (numsels == 0 || numsels > 8192), InternalError
 *      If (selValidateLDT(pptda, sel, csel) != NO_ERROR)
 *          return error
 *      Endif
 *      Ifdef   SELSTRICT
 *          Convert selector to offset into LDT
 *          Do (numsels times)
 *              If (LDTAddr.sel.d_access)       ; selector in use?
 *                  InternalError
 *              Endif
 *              Set sel = + SEL_INCR
 *      Endif SELSTRICT
 *      return (NO_ERROR)
 */
int         selAllocLDTSub(sel, csel, pptda)
SEL         sel;                    /* Selector */
ushort_t    csel;                   /* No. of selectors */
struct      ptda_s  *pptda;         /* Address of ptda */
{
#ifdef SELSTRICT
    register struct  desctab  *pdesc;        /* Pointer to LDT */
#endif
    int     rc;

        /* Internal error if csel is 0 or larger than 8192 */
    selAssert ((csel != 0) && (csel <= SEL_MAXDESC));

        /* Make sure there is a valid LDT */
    selAssert(pptda->ptda_ldthandle);

    if ((rc = selValidateLDT(pptda, sel, csel)) != NO_ERROR)
        return(rc);

#ifdef SELSTRICT
    pdesc = (struct desctab *) pptda->ptda_ldtaddr +
        (int) (sel >> SEL_SHIFT);
    do {
        selAssert (pdesc->d_access == 0);
        pdesc++;
    } while  (--csel);
#endif
    return(NO_ERROR);
}

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

/***EP  SELAllocLDT - Allocate LDT selector
 *
 *      SELAllocLDT allocates selector(s) from the passed task's LDT.
 *      This procedure should only be used for specific allocations.
 *      Nonspecific allocations should use SELReserveLDT.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   flags   - shared/private (NO LONGER USED)
 *              sel     - Base selector value to be allocated
 *              numsels - No. of selectors HUGEINCR apart to be allocated
 *              hptda   - Context handle (=0 if current context)
 *      RETURN  int     -  error code (NO_ERROR if no error)
 *
 *      SELAllocLDT (ulong flags, SEL sel, ushort_t numsels, VMHOB hptda)
 *
 *      PseudoCode:
 *
 *      Call VMGetPTDAAddr
 *      Call selGetLDTSub
 */
int         SELAllocLDT(fl, selector, csel, hptda)
ulong_t     fl;                     /* not used any more */
SEL         selector;               /* base selector value to allocate */
ushort_t    csel;                   /* No. of selectors */
VMHOB       hptda;                  /* PTDA handle */
{
    int rc;                         /* return code, used by Performance Hook */

    MMPHPreSELAllocLDT()            /* Performance Hook (20/28) */

    rc=selAllocLDTSub(selector, csel, VMGetPTDAAddr(hptda));

    MMPHPostSELAllocLDT()           /* Performance Hook (20/A8) */
    return (rc);
}

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

/***EP  SELReserveLDT - Reserve selector(s) and virtual memory.
 *
 *      SELReserveLDT reserves selector(s) and corresponding virtual
 *      memory from a task's LDT address space. It should be called
 *      to allocate call gates, and other, nonspecific LDT allocations.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   flags   - allocate from shared/private region
 *                  SELSD_SHARED Allocate shared LDT seletor
 *                          0   Allocate private LDT selector
 *                          1   Use selector given in psel
 *              numsels - no. of selectors to allocate
 *              hptda   - handle to task's ptda
 *              psel   - pointer to location to write base selector value
 *      RETURN  int     - error code (NO_ERROR if no error)
 *
 *      SELReserveLDT(ulong flags, ushort numsels, VMHOB hptda, void *psel)
 *
 *      PseudoCode:
 *
 *      Call VMReserve (shared/private LDT space, npages=numsels*16,
 *              64K boundary, hobptda)
 *      If (error), return (ERROR_NOT_ENOUGH_MEMORY)
 *      Call SELAllocLDT (specific, numsels, returned laddr to selector)
 *      If (error)
 *          Call VMVirtFree
 *          return (error code)
 *      Endif
 *      Write *psel = base selector
 *      return (NO_ERROR)
 */
int     SELReserveLDT(fl, csel, hptda, psel)
ulong_t     fl;                 /* shared/private/specific flag */
ushort_t    csel;               /* no. of selectors to reserve */
VMHOB       hptda;              /* ptda handle */
SEL         *psel;
{
    int     rc;                 /* return code */
    VMAR    *par;               /* pointer to arena record */
    ulong_t va = 0;
    ulong_t flres;              /* flags for VMReserve */

    if (fl == -1) {         /* specific value ? */
        selAssert(*psel & SEL_LDTBIT);
        flres  = SRL_RESERVE | VMAC_LOCSPECIFIC | VMAC_ARENAPRV;
        va = SelToLaTiled(*psel);    /* set up specific address */
        if (VMShrTiledArena(va)) {
         /*If shared specific, no need to reserve memory, just alloc selector */
            return(SELAllocLDT(SELSD_SHARED, *psel, csel, hptda));
        }
    }
    else {
        if (fl & SELSD_SHARED) {
            flres=SRL_RESERVE | VMAC_LOCANY | VMAC_ARENASHR;
        }
        else {
            flres = SRL_RESERVE | VMAC_LOCANY | VMAC_ARENAPRV;
        }
    }
    if ((rc = VMReserve((((ulong_t) csel) << (WORDSHIFT - PAGESHIFT)),
        flres, VMGetPTDAAddr(hptda), va >> PAGESHIFT, 0, SSToDS(&par)))
        != NO_ERROR) return(rc);
    selAssert((par->ar_ipg & (WORDMASK >> PAGESHIFT)) == 0);
    *psel = IpgToSelTiled((par->ar_ipg)) | (SEL) RPL_RING3;
    if ((rc = SELAllocLDT(fl, *psel, csel, hptda)) != NO_ERROR)
        VMFreeMem(par->ar_ipg, hptda, NA);
    return(rc);
}

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

/***LP  selFreeLDTSub - Free LDT selector, return total descriptor size
 *
 *      selFreeLDTSub frees the range of LDT selectors, returning
 *      their total descriptor size. If call gate selector, it also
 *      dereserves memory.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY       sel     - Base selector
 *                  csel    - No of selectors to be freed
 *                  pptda   - Address of ptda
 *      RETURN      ulong_t - Total descriptor size (-1 if totally invalid)
 *
 *      PseudoCode:
 *
 *      Set csel = -1
 *      Change selector to offset
 *      If (gate selector)
 *          Call VMFreeMem
 *          return (csel)
 *      Do (csel) times
 *          If (sel.d_access == 0)
 *              break
 *          Set sel.d_access = 0
 *          Assert(previous selector's limit == 64K - 1)
 *          Add SEL_INCR to sel
 *      cbdesc = (previous sel - initial sel - 1)*64K + previous sel's limit+1
 *      Assert{
 *          From current sel. to last sel
 *              sel.d_access == 0
 *      }
 */
ulong_t selFreeLDTSub(sel, csel, pptda)
SEL         sel;                        /* base selector to be freed */
ushort_t    csel;                       /* No. of selectors */
struct      ptda_s *pptda;              /* Address of ptda */
{
    ulong_t          cbdesc = -1;       /* initialize to invalid value */
    register struct  desctab   *pdesc;    /* pointer to descriptor */
    register struct  desctab   *pdescmax; /* pointer to (last + 1)descriptor */
    struct  desctab *pdescmin;            /* Pointer to first descriptor */

    selAssert((csel != 0) && (csel < SEL_MAXDESC));
    selAssert(sel & SEL_LDTBIT);
    if(pptda->ptda_ldtaddr == NA)
    {
        return(0);
    }
    pdesc = (struct desctab *)(pptda->ptda_ldtaddr) + (int) (sel >> SEL_SHIFT);
        /* pdesc points to descriptor */

    if ((((pdesc->d_access) & (D_SEG | D_GATE)) == D_GATE)) {
        if (!(VMShrTiledArena((SelToLaTiled(sel)))))
            VMFreeMem(SelToLaTiled(sel), pptda->ptda_handle, NULL);
        selAssert(csel == 1);
        return(cbdesc);             /* Indicate invalid committed size */
    }

    pdescmin = pdesc;
    pdescmax = pdesc + csel;
    do  {
        if (pdesc->d_access == 0) {
            break;
        }
        pdesc->d_access = 0;

    } while (++pdesc < pdescmax);

    /* We either found the first invalid descriptor or
     * invalidated all descriptors
     */
    if (pdesc != pdescmin) {

        /* Calculate committed size if not totally sparse.
         * pdesc points to first invalid descriptor, or pdescmax
         * if all descriptors are valid.
         */

        cbdesc = (((ulong_t)(pdesc - pdescmin) - 1) << WORDSHIFT) +
                (ulong_t)((pdesc - 1)->d_limit) + 1;
    }
#ifdef SELSTRICT
    /* Make sure that all the remaining descriptors in the range are
     * invalid
     */
    while (++pdesc < pdescmax) {
        selAssert(pdesc->d_access == 0);
    }
#endif
    return(cbdesc);
}


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

/***EP  SELFreeLDT - Free LDT selector
 *
 *      SELFreeLDT marks the descriptor(s) associated with the passed
 *      LDT selector(s) as unused. If call gate selector, it also dereserves
 *      memory.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY       sel     - Base selector
 *                  csel    - No of selectors to be freed
 *                  hptda   - context handle
 *      RETURN      NONE
 *
 *      PseudoCode:
 *
 *      Call selGetLDTAddr(context handle)
 *      Change selector to offset
 *      While (LDT page corresponding to the selector is valid,
 *          && (Numsel != 0))
 *          set csel = min(Numsel, no of sels to nearest page bound.)
 *          Do (csel) times
 *              If (gate selector) Call VMFree
 *              Set sel.d_access = 0
 *              Add SEL_INCR to sel
 *          Set Numsel = Numsel - csel
 */
void        SELFreeLDT(selector, csel, hptda)
SEL         selector;                   /* base selector to be freed */
ushort_t    csel;                       /* No. of selectors */
VMHOB       hptda;                      /* context handle */
{
    int              rc;
    register struct  ptda_s    *pptda = VMGetPTDAAddr(hptda);
           /* pointer to ptda */
    register struct  desctab   *pdesc;       /* pointer to descriptor */
    register struct  desctab   *pdescmax;    /* pointer to descriptor */


    MMPHPreSELFreeLDT()                      /* Performance Hook 20/29) */

    selAssert((csel != 0) && (csel < SEL_MAXDESC));
    selAssert(selector & SEL_LDTBIT);
    /* Make sure the LDT region is valid */
    pdesc = selGetLDTAddr(hptda);
    pdesc += (int) (selector >> SEL_SHIFT);  /* pdesc points to descriptor */
    pdescmax = pdesc + csel;

    do  {

        /* Terminate loop if we hit an invalid region */
        if (!(selIsValidLDTsel(pptda, selector))) break;

        if ((((pdesc->d_access) & (D_SEG | D_GATE)) == D_GATE)) {
            if (!(VMShrTiledArena((SelToLaTiled(selector)))))
                VMFreeMem(SelToLaTiled(selector), hptda, NULL);
        }
        selector += SEL_INCR;
        pdesc->d_access = 0;
        pdesc++;
    } while (pdesc < pdescmax);

    MMPHPostSELFreeLDT()                     /* Performance Hook 20/A9) */
}


#pragma code_seg()

/***OP  SELObjectInfo - Stabilize segment, then return descriptor and object
 *                      information.
 *
 *      OBSOLESCENT!  Use SELGetOd
 *
 *      WARNING:  This function is retained only to support existing callers.
 *                All callers should be converted to call SELGetOd
 *
 *      SELObjectInfo waits for the segment to be stabilized (object semaphore
 *      available). Then returns descriptor and object information.
 *      If requested, it will also take object semaphore. It returns error
 *      if object semaphore is not available and the no wait option
 *      is specified.
 *      The selector passed does not have to map from the beginning of the
 *      object. If not, then the address returned in the descriptor info
 *      will be different than the object's address returned in object info.
 *      BUGBUG: If nobody is using this procedure with VMGS_NOBLOCK flag, this
 *      procedure can also be in swappable code
 *
 *      ENTRY   fl      - Semaphore flags (same as VMGS_*)
 *              sel     - selector to the object
 *              hptda   - ptda handle (needed only if LDT selector, 0=current)
 *              pdesc   - pointer to location to return descriptor info
 *              poi     - pointer to location to return object info
 *      RETURN  int     - error code
 *
 *      int SELObjectInfo(ulong_t fl, SEL sel, VMHOB hptda,
 *                  struct seldesc_s *pdesc, struct vmoi_s *poi)
 *
 *      PseudoCode:
 *
 *      Call SELGetOd to get the object data.  If successful,
 *      fill in the object info structure.
 *      Return rc from SELGetOd to caller.
 */

//int     _stdcall SELObjectInfo(ulong_t fl, SEL sel, VMHOB hobPTDA,
//                               struct seldesc_s *pdesc, struct vmoi_s*poi)
//                            /* VMGetSem flags */
//                            /* selector of the object */
//                            /* context handle (only if LDT selector, 0=current) */
//                            /* location to return descriptor info */
//                            /* location to return object info */
//{
//    int     rc;                 /* return code */
//    VMOD    od;                 /* Object data */
//
//    /*
//     *  If GDT selector, the value of hobPTDA passed in is indeterminate
//     *  so set it to HOBNULL.
//     */
//
//    if(sel & SEL_LDTBIT)
//      {
//        od.od_pPTDA = VMGetPTDAAddr(hobPTDA);
//      }
//    else
//      {
//        hobPTDA = HOBNULL;
//        od.od_pPTDA = NULL;
//      }
//    if((rc = SELGetOd(fl, sel, pdesc, SSToDS(&od))) == NO_ERROR)
//      {
//        VMOdToOi(SSToDS(&od), poi);
//      }
//    return(rc);
//}


/***EP  SELGetOd - Stabilize segment, then return descriptor and object
 *                 info.
 *
 *      SELGetOd waits for the segment to be stabilized (object semaphore
 *      available). Then returns descriptor and object information.  If
 *      requested, SELGetOd will also take object semaphore. SELGetOd will
 *      return error if the object semaphore is not available and the no wait
 *      or error-if-block option is specified.
 *
 *      The selector passed does not have to map from the beginning of the
 *      object. If not, then the address returned in the descriptor info
 *      will be different than the object's address returned in object info.
 *      BUGBUG: If nobody is using this procedure with VMGS_NOBLOCK flag, this
 *      procedure can also be in swappable code
 *
 *      ENTRY           fl              - Semaphore flags (same as VMGS_*)
 *                      sel             - Selector to the object
 *                      pdesc           - Pointer to location in which
 *                                        descriptor info will be returned
 *                      pod             - Pointer to location in which object
 *                                        info will be returned
 *      RETURN          int             - Result code (NO_ERROR = success)
 *
 *      int SELGetOd(ulong_t fl, SEL sel, struct seldesc_s *pdesc, VMOD *pod)
 *
 *      PseudoCode:
 *
 *      Turn on ERRBLOCK flag if NOBLOCK flag is specified
 *      Get linear address mapped by selector
 *      Call VMGetOd to get object data optionally after object is stable
 *        or sem taken
 *      Call SELGetDescInfo to get descriptor info
 *      return result code
 */

int     SELGetOd(fl, sel, pdesc, pod)
ulong_t                 fl;             /* VMGetSem flags */
SEL                     sel;            /* Selector of the object */
struct seldesc_s        *pdesc;         /* Return desc info pointer */
VMOD                    *pod;           /* Return object info pointer */
{
  VMHOB                 hobPTDA;        /* PTDA pseudo-handle */
  int                   rc;             /* Result code */

  /*
   *  If we are allowed to block (within VMGetOd), set VMGS_ERRBLOCK flag
   *  so that we know to repeat the loop as the object may have changed.
   */

  if(!(fl & VMGS_NOBLOCK))
    {
      fl |= VMGS_ERRBLOCK;
    }

  /*
   *  If a GDT selector or no PTDA pointer given, specify the current PTDA.
   */

  if((!(sel & SEL_LDTBIT)) || pod->od_pPTDA == NULL)
    {
      pod->od_pPTDA = pPTDACur;
    }

  /*
   *  Get the linear address mapped by the selector.  WARNING: An invalid
   *  LDT selector may still have a valid base address, so the ensuing
   *  call to VMGetOd may succeed!
   */

  if(sel & SEL_LDTBIT)
    {
      pod->od_laddr = SELSelToLa((ulong_t)sel, pod->od_pPTDA);
    }

  else
    {
      pod->od_laddr = SELVirtToLin(0, sel);
    }

  /*
   *  Get object info, waiting for object to stabilize or taking semaphore.
   *  For non-system arena objects, the object info can be requested either
   *  for a specific context or any context found (shared content only)
   */

  while((rc = VMGetOd(fl, pod)) ==  ERROR_BLOCKED);

  /*
   *  Get the descriptor info.
   */

  if(rc == NO_ERROR)
    {
      hobPTDA = VMHobPTDAOd(*pod);
      if((rc = SELGetDescInfo(sel, hobPTDA, pdesc)) != NO_ERROR)
        {
          if(fl & VMGS_TAKESEM)
            {
              VMClearSem(VMHobOd(*pod));
            }
          if(fl & VMOD_PTDA_SEM)
            {
              VMClearSem(hobPTDA);
            }
        }
    }
  return(rc);
}

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

/***EP  SELGetSize - Obtain the true size of the object
 *
 *      SELGetSize returns the true size of the object. The object
 *      semaphore must be obtained in order to avoid reading size in the
 *      middle of a realloc.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   laddr   - linear address of the object
 *              sel     - sel (if GDT)
 *              hptda   - Handle to ptda (only if LDT)
 *              cbmax   - maximum size of the object (AR size)(0 if not known)
 *              pcb     - location to write the size
 *      RETURN  int     - error code (NO_ERROR if no error)
 *
 *      SELGetSize (ulong laddr, SEL sel, VMHOB hptda, ulong cbmax,
 *                  void *pcb)
 *
 *      PseudoCode:
 *
 *      if (cbmax == 0) Call VMObjectInfo
 *      If (laddr in system area)
 *          Call _SELGetDescInfo
 *          If (no error && D_SEG)
 *              return (size from descriptor)
 *          Else
 *              return (cbmax)
 *          Endif
 *      Else                    ; user area
 *          If (laddr + cbmax > 512Meg), return (cbmax)
 *          For (sel = LaToSel(laddr); SelToLa(sel) < laddr+cbmax;
 *              sel +=SEL_INCR)
 *              if (GETDescInfo(sel) fails), break out of loop
 *          if (GetDescInfo failed in first try)
 *              return(cbmax)
 *          else
 *              return(desc_laddr + desc_cb - laddr)
 *          endif
 *      Endif
 */
int         SELGetSize(laddr, sel, hptda, cbmax, pcb)
ulong_t     laddr;
SEL         sel;
VMHOB       hptda;
ulong_t     cbmax;
ulong_t     *pcb;
{
    int     rc;                     /* return code */
//  VMOI    oi;                     /* object information buffer */
    VMOD    od;                     /* object information buffer */
    struct seldesc_s  desc;         /* Descriptor information */

    if (cbmax == 0)
    {
//      if ((rc = VMObjectInfo(laddr, hptda, SSToDS(&oi))) != NO_ERROR)

        od.od_laddr = laddr;
        od.od_pPTDA = VMGetPTDAAddr(hptda);
        if ((rc = VMGetOd(NA, SSToDS(&od))) != NO_ERROR)
        {
            return (rc);
        }
        else
        {
//          cbmax = oi.oi_cb;
            cbmax = (od.od_par)->ar_cpg << PAGESHIFT;
        }
    }

    /*
     *  KDH -
     *  If the address is in the system arena or is a packed object, just
     *  yank the size out of the descriptor.  We need to check both "fences":
     *  one in the private arena, and one in the shared arena.  For now, we
     *  assume that the selector in question is NOT in the protected region.
     *  But if it is in the protected region, this code should still work, but
     *  at a slower performance.
     */
    if (!(VMTiled((laddr + cbmax))) ||
        sel >  VMGetPTDAAddr(hptda)->ptda_infoseg.LIS_PackShrSel ||
        sel < VMGetPTDAAddr(hptda)->ptda_infoseg.LIS_PackSel)  {

            if (rc = SELGetDescInfo(sel, hptda, SSToDS(&desc)) == NO_ERROR)
                *pcb = desc.desc_cb;
            else
                *pcb = cbmax;

    /*
     *  If this is an ldt segment that is not packed, we might be
     *  dealing with a huge object.  So we have to do a little more work.
     */
    } else   {
        for (sel = LaToSelTiled(laddr) ; SelToLaTiled(sel) < (laddr + cbmax) ;
            sel += SEL_INCR) {
            if (SELGetDescInfo(sel, hptda, SSToDS(&desc)) != NO_ERROR)
                break;
        }
        /* return 0 if SELGetDescInfo failed in first try */
        *pcb = (SelToLaTiled(sel) > laddr) ?
            desc.desc_laddr + desc.desc_cb - laddr : 0;
        selAssert(*pcb <= cbmax);
    }
    return(NO_ERROR);
}

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

/***EP  SELCopy - Copy descriptors from one context to another.
 *
 *      SELCopy copies descriptors from one context to another, growing
 *      the target LDT if necessary. It doesn't check for the validity
 *      of the contents of descriptors, nor does it check to see if the
 *      source LDT is large enough.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   sel         - base selector
 *              csel        - csel, no. of selectors to copy
 *              hptdasrc    - hptdasrc, source context
 *              hptdadest   - destination context
 *              fl          - Read/write/exec permissions
 *      RETURN              error code, (NO_ERROR if no error)
 *
 *      SELCopy(SEL sel, ushort csel, VMHOB hptdasrc, VMHOB hptdadest)
 *
 *      PseudoCode:
 *
 *      If (selValidateLDT(hptda, sel, csel) != NO_ERROR)
 *          return error
 *      Endif
 *      Call selGetLDTAddr(hptdadest)
 *      Call selGetLDTAddr(hptdasrc)
 *      Set src=pLDTsrc+(sel & not(SEL_LDTBIT+SEL_RPLCLR))
 *      Set dest=pLDTdest+(sel & not(SEL_LDTBIT + SEL_RPLCLR))
 *      Do csel times
 *          Copy (size desctab) bytes from src to dest
 *          add HUGEINCR to src and dest
 *      return(NO_ERROR)
 */
int         SELCopy(sel, csel, hptdasrc, hptdadest, fl)
SEL         sel;
ushort_t    csel;
VMHOB       hptdasrc;
VMHOB       hptdadest;
ulong_t     fl;
{
    int     rc;
    struct  ptda_s  *pptdadest = VMGetPTDAAddr(hptdadest);
    register struct  desc32  *pdescsrc;
    register struct  desc32  *pdescdest;
    ulong_t f1 = 0, f2 = -1;

    /* Make sure  0 < csel <= 8192  */
    selAssert((csel != 0) && (csel <= SEL_MAXDESC));

    /* Make sure target LDT region is valid */
    if(pptdadest->ptda_ProcType == PT_VDM)
    {
        rc = SELAllocVdmSels(csel, SSToDS(&csel), SSToDS(&sel));
    }
    else
    {
        rc = selValidateLDT(pptdadest, sel, csel);
    }
    if (rc != NO_ERROR)
    {
        return(rc);
    }

    pdescsrc = selGetLDTAddr(hptdasrc);
    pdescsrc += (int) (sel >> SEL_SHIFT);
    pdescdest = selGetLDTAddr(hptdadest);
    pdescdest += (int) (sel >> SEL_SHIFT);

    /* See if we have to increase/decrease permissions */

    /* BUGBUG: Loader is reckless about setting PG_X flag in the allocation.
     * so if it is a code segment and PG_R flag is set, don't turn off
     * the D_RX flag
     */
    if ((fl & PG_W) ||
        ((((struct desctab *)pdescsrc)->d_access & D_CODE) &&
        (fl & PG_R)))
    {
        f1 = D_W << BYTESHIFT;
        selAssert (D_W == D_RX);
    }
    else {
        f2 = ~(D_W << BYTESHIFT);
    }

    for (csel ; csel ; --csel, pdescdest++, pdescsrc++) /* copy selectors */
    {
        pdescdest->d32_low  = pdescsrc->d32_low;
        pdescdest->d32_high = pdescsrc->d32_high;

        if (((struct desctab *)pdescdest)->d_access != NULL)
        {
            pdescdest->d32_high &= f2;   /* may remove access permissions */
            pdescdest->d32_high |= f1;   /* may add access permissions */
        }
    }
    return(NO_ERROR);
}

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

/***EP  SELAddPermission - Increase descriptor access permissions.
 *
 *      SELAddPermission searches for valid descriptors mapping the given
 *      address range, and 'or's the given permissions to descriptor's
 *      access byte. It only increases access permissions, never
 *      decrease them.
 *      WARNING: This routine is in swappable code
 *
 *      ENTRY   ipg     - Page number
 *              pptda   - pointer to ptda
 *              cpg     - No. of pages
 *              fl      - Permission levels (PG_R, PG_W, or PG_X)
 *      EXIT    NONE
 */
void    SELAddPermission(ipg, pptda, cpg, fl)
ulong_t ipg;
struct  ptda_s  *pptda;
ulong_t cpg;
ulong_t fl;
{

    register struct  desctab  *pdesc;
    register int     cdesc;

    /*
     * Note that we only increase the permissions, and we only do it for
     * valid descriptors mapping the range. So if the specified permissions
     * are read only data, or execute only code; we can return without doing
     * anything.
     */

    /* BUGBUG: Don't bother checking for PG_X flag, because people are
     * reckless about setting this flag.
     */

    pdesc = (struct desctab *)(pptda->ptda_ldtaddr);
    pdesc += ipg >> (SEL_IPG_SHIFT + SEL_SHIFT);
    if ((fl & PG_W)  ||
        ((fl & PG_R) && (pdesc->d_access & D_CODE)))  {
        cdesc = ((ipg + cpg - 1) >> (SEL_IPG_SHIFT + SEL_SHIFT))
            - (ipg >> (SEL_IPG_SHIFT + SEL_SHIFT)) + 1;

        /* Make sure we don't go past LDT */
        selAssert(((ulong_t)(pdesc + cdesc) - (ulong_t)pptda->ptda_ldtaddr)
            < SEL_64K);

        while (cdesc-- > 0) {
            if (pdesc->d_access != NULL) {
                pdesc->d_access |= D_W;
            }
            selAssert(D_W == D_RX);
            pdesc++;
        }
    }
}


#pragma code_seg()

/***EP  SELLdrDesc - Map linear address by using the given selector or
 *           allocating a new one.
 *
 *      SELLdrDesc allocates a GDT selector if not given, and maps the
 *      associated address as ring 0, r/w data for the loader component.
 *      This selector should be freed via SELFreeGDT.
 *      WARNING:If task linear addresses are used, the addresses
 *      will point to hyperspace outside the context of given task.
 *
 *      Algorithm:
 *          - Allocate a GDT selector
 *          - Set descriptor information as desired
 *
 *      ENTRY   laddr - linear address to map
 *                 cb - size
 *               psel - pointer to location to write/read the selector
 *      RETURN    int - error code      (NO_ERROR if no error)
 *
 *      SELLdrDesc(ulong_t laddr, ulong_t cb, SEL *psel)
 *
 *      PseudoCode:
 *
 *      If (*psel == 0)
 *          Call SELAllocGDT
 *      Endif
 *      Call SELSetDescInfo (ring0, data, r/w, D_UVIRT clear)
 *      return (selector)
 */
int         SELLdrDesc(laddr, cb, psel)
ulong_t     laddr;
ulong_t     cb;
SEL         *psel;
{
    int     rc;                         /* return code */

    if (*psel == 0)
        if ((rc = SELAllocGDT(psel)) != NO_ERROR) return(rc);
    SELSetDescInfo(SELSD_ALL, D_DATA0, *psel, cb, laddr, 0, 0, 0);
    return(NO_ERROR);
}


/***EP  SELMapVirt - map a memory object into the GDT as ring 0 data
 *
 *      SELMapVirt maps a memory object into the passed GDT selector
 *      as ring 0 data.
 *      WARNING:If task linear addresses are used, the addresses
 *      will point to hyperspace outside the context of given task.
 *
 *      ENTRY   laddr - linear address of the object
 *              size  - size of the object to map
 *              sel   - GDT selector to map
 *      RETURN  int   - Error code (NO_ERROR if no error)
 *
 *      SELMapVirt(ulong laddr, ulong size, SEL sel)
 *
 *      PseudoCode:
 *
 *      Call SELGetDescInfo(GDT sel)
 *      If (no error && (access != 0 || (not D_UVIRT)))
 *          return (ERROR_ACCESS_DENIED)
 *      Endif
 *      Call SELSetDescInfo(sel, laddr, size, SD_ALL, D_UVIRT, RING0 DATA)
 *      return(NO_ERROR)
 */
int         SELMapVirt(laddr, cb, sel)
ulong_t     laddr;
ulong_t     cb;
SEL         sel;
{
    int     rc;
    struct seldesc_s    dsc;

    if ((rc = SELGetDescInfo(sel, 0, SSToDS(&dsc))) == NO_ERROR) {
        /* Make sure that it is D_UVIRT */
        if (!(dsc.desc_attr & D_UVIRT)) return (ERROR_ACCESS_DENIED);
    } else  /* Make sure it doesn't extend beyond GDT limit */
        if (sel > SELMaxGDT) return(ERROR_ACCESS_DENIED);
    SELSetDescInfo(SELSD_ALL, ((D_UVIRT << BYTESHIFT) | D_DATA0), sel,
        cb, laddr, 0, 0, 0);
    return(NO_ERROR);
}


/***EP  SELGetSelector - Obtain the selector of the given handle
 *
 *      SELGetSelector obtains the base selector value of the given handle.
 *
 *      ENTRY   hob     - handle
 *              psel    - Location to return selector
 *      RETURN  int     - error code (NO_ERROR if no error)
 *
 *      SELGetSelector(VMHOB hob, SEL *psel)
 *
 *      PseudoCode:
 *
 *      Call VMGetLaddr
 *      Call SELConvertToSelector
 *      return
 */
int        SELGetSelector(hob, psel)
VMHOB      hob;                     /* handle to object record */
SEL        *psel;                   /* Location to write the selector */
{
    int     rc;                     /* return code */
    ulong_t laddr;                  /* address of object */
    VMHOB   hptda;                  /* object context */

    if ((rc = VMObjHandleInfo(hob, SSToDS(&laddr), SSToDS(&hptda)))
        == NO_ERROR) {
        *psel = SELConvertToSelector(laddr, (uchar_t) -1, hptda);
    }
    return(rc);
}

/***EP  SELSetAccess - Mark LDT descriptors mapping to video buffers
 *
 *      SELSetAccess is called by VMValidate to mark the LDT descriptors
 *      mapping to video buffers read only/read write
 *      WARNING: This procedure is in swappable code (B732376 - moved resident)
 *      ##BUGBUG - This routine moved to resident code until tkScanTasks
 *                 can be preempted. tkScanTasks calls vmSGSValidate which
 *                 calls this routine.
 *
 *      ENTRY       ipg     - Page address of the region
 *                  pptda   - Pointer to ptda
 *                  cpg     - No of pages in the region
 *                  fl      - Flags (VMSGS_ADD/VMSGS_REMOVE)
 *      EXIT        NONE
 */
void        SELSetAccess(ipg, pptda, cpg, fl)
ulong_t     ipg;            /* Page address of region */
struct ptda_s   *pptda;     /* Address of ptda */
ulong_t     cpg;            /* No of pages */
ulong_t     fl;             /* Flags */
{
    register struct  desctab  *pdesc = (struct desctab *)(pptda->ptda_ldtaddr +
                            (ipg >> SEL_IPG_SHIFT));
    register struct desctab *pdmax;

    pdmax = pdesc + (cpg >> (SEL_IPG_SHIFT + SEL_SHIFT));
    selAssert ((ipg & ~SEL_IPG_MASK) == NULL);
        /* Must start on a 64Kb boundary */
    selAssert ((cpg & ~SEL_IPG_MASK) == NULL);
        /* Size must be a multiple of 64Kb */
    for (; pdesc < pdmax; pdesc++) {
        /* Make sure it is a data selector */
        selAssert((pdesc->d_access & (D_SEG + D_CODE)) == D_SEG);
        selAssert(VMSGS_ADD);
        if (fl & VMSGS_ADD)
            pdesc->d_access |= D_W;
        else        /* remove request */
            pdesc->d_access &= ~D_W;
    }
}

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


/***EP  SELCheckAccess - Check access privileges of the given address range
 *
 *      SELCheckAccess checks the descriptor access privileges of the given
 *      virtual address range.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   fl  - Flags
 *                      SELCA_W : Check write access(read only if not set)
 *                      SELCA_U : Check user level (Ring 3) access
 *                      SELCA_IOPL : Check IOPL level (Ring 2 or 3) access
 *              sel -   Selector
 *              offs-   Offset
 *              cb  -   size of address range
 *              hptda-  Context (required only if LDT sel)
 *      EXIT    int - NO_ERROR if range has requested access
 *
 */
int     SELCheckAccess(fl, sel, offs, cb, hptda)
ulong_t fl;         /* SELCA_W, SELCA_U, SELCA_IOPL */
SEL     sel;        /* selector */
ulong_t offs;       /* Offset to the start of region */
ulong_t cb;         /* Size of region */
VMHOB   hptda;      /* context handle */
{
    int rc;
    struct seldesc_s  d;

    if ((rc = SELGetDescInfo(sel, hptda, SSToDS(&d))) != NO_ERROR)
        return(rc);
    if (!(d.desc_acc & D_SEG)               /* If access= 0 */
        || ((fl & SELCA_W) && ((d.desc_acc & D_CODE) || !(d.desc_acc & D_W)))
                                    /* If write requested and r only desc. */
        || ((fl & SELCA_PRIVMASK) > (d.desc_acc & D_PRIV)))
                /* If requested privilege doesn't match */
        return(ERROR_ACCESS_DENIED);
    selAssert(SELCA_PRIVMASK == D_PRIV);
    if ((d.desc_acc & (D_CODE | D_EXPDN)) == D_EXPDN) {
        selAssert(!(d.desc_attr & D_GRAN4K));
        if ((offs < (SEL_64K - d.desc_cb)) || ((offs + cb) > SEL_64K))
            return (ERROR_ACCESS_DENIED);
    } else {
        if ((offs + cb) > d.desc_cb)
            return(ERROR_ACCESS_DENIED);
    }
    return(NO_ERROR);
}


/***LP  SELSuBuff - Set user buffer via a 16:16 pointer
 *
 *      SELSuBuff stores given range to the user area.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   pdst            - 16:16 pointer to user buffer
 *              psrc            - 0:32 pointer to kernel buffer
 *              cb              - number of bytes to copy
 *              ffatal          - fatal-on-failure flag (TK_UM_FATAL or NULL)
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *      PseudoCode
 *
 *      Call SELCheckAccess to check privilege and access
 *      Call TKSuBuff
 */
int                     SELSuBuff(pdst,psrc,cb,ffatal)
struct FarPtr           pdst;           /* Destination in user space */
void                    *psrc;          /* Source in kernel space */
ulong_t                 cb;             /* Number of bytes to copy */
ulong_t                 ffatal;         /* Fatal-on-failure flag */
{
    ulong_t             pla = SELVirtToLin((ulong_t) pdst.Offst, pdst.Segmt);

    union {
        struct FarPtr   p16;
        ulong_t         p;
    } FaultCast;

    if (SELCheckAccess(SELCA_W | SELCA_IOPL, pdst.Segmt,
        (ulong_t) pdst.Offst, cb, HOBNULL) != NO_ERROR) {
        if (ffatal & TK_UM_FATAL) {
            FaultCast.p16 = pdst;
            TKUsrMemGPFault(FaultCast.p);
        }
        return(ERROR_PROTECTION_VIOLATION);
    }

    return(TKSuBuff((char *) pla,
        psrc, (int) cb, (int) ffatal));
                                        /* Copy to user space */
}

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

/***LP  SELFuBuff - Fetch user buffer via a 16:16 pointer
 *
 *      SELFuBuff stores given range to the user area.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   pdst            - 0:32 pointer to kernel buffer
 *              psrc            - 16:16 pointer to user buffer
 *              cb              - number of bytes to copy
 *              ffatal          - fatal-on-failure flag (TK_UM_FATAL or NULL)
 *      RETURN  int             - return code (NO_ERROR if successful)
 *
 *      PseudoCode
 *
 *      Call SELCheckAccess to check privilege and access
 *      Call TKFuBuff
 */
int                     SELFuBuff(pdst,psrc,cb,ffatal)
void                    *pdst;          /* Destination in kernel space */
struct FarPtr           psrc;           /* Source in user space */
ulong_t                 cb;             /* Number of bytes to copy */
ulong_t                 ffatal;         /* Fatal-on-failure flag */
{
    ulong_t             pla = SELVirtToLin((ulong_t) psrc.Offst, psrc.Segmt);

    union {
        struct FarPtr   p16;
        ulong_t         p;
    } FaultCast;

    if (SELCheckAccess(SELCA_IOPL, psrc.Segmt,
        (ulong_t) psrc.Offst, cb, HOBNULL) != NO_ERROR) {
        if (ffatal & TK_UM_FATAL) {
            FaultCast.p16 = psrc;
            TKUsrMemGPFault(FaultCast.p);
        }
        return(ERROR_PROTECTION_VIOLATION);
    }

    return(TKFuBuff(pdst, (char *) pla, (int) cb, (int) ffatal));
                                        /* Copy to kernel space */
}

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

/***LP  selInitLDTSub - Allocate and initialize LDT
 *
 *      selInitLDTSub allocates a task's LDT. All LDT's are allocated as
 *      sparse objects with full 64K size. The pages within an LDT are
 *      validated as required. Initially all LDT's have two valid pages
 *      except for vdm processes, which have only the first page valid.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   hptda - Context (Linear address of ptda for whom the LDT
 *                      is to be allocated.)
 *              pladdr- address to return LDT address
 *      RETURN  int   - Error code (NO_ERROR if no error)
 *
 *      selInitLDTSub(VMHOB hptda, ulong pladdr)
 *
 *      PseudoCode:
 *
 *      Call VMAllocMem(zero initialized, discardable, hmte=DISCARDOWNER,
 *          size=64K,SELLDTOWNER)
 *      Call PGSetTypeLDT(swappable, zeroinit, 1st page) ;Must zero initialize
 *      Call PGSetTypeLDT(swappable, zeroinit, infoseg page)
 *      ; Set up ptda parameters
 *      Set [ptda_ldtpgmap] = SEL_LDTPGMAPINIT  ; only two pages are valid
 *      Set [ptda_ldthandle] = handle
 *      Set [ptda_ldtaddr] = address
 *      return
 */

int
selInitLDTSub(VMHOB hptda,
              ulong_t *pladdr)
{
    int     rc;                               /* return code */
    register struct ptda_s *pptda=VMGetPTDAAddr(hptda); /* ptda address */
    VMAC    ac;                     /* VMAlloc info */
// lotus
#ifdef PMDEBUG
;    _asm int 3;
#endif
// lotus
    if ((rc = VMAllocMem(SEL_64K, SEL_64K, SEL_PGLDT1 | SEL_VMLDT, hptda,
        SELLDTOWNER, DISCARDOWNER, NULL, PGB_LDT, SSToDS(&ac)))
         == NO_ERROR)  {
        if ( ((rc = PGSetTypeLDT (ac.ac_va >> PAGESHIFT,
                                  ac.ac_hob,0,SEL_PGLDT2)) != NO_ERROR)
            ||
// GSR start
              ((rc = PGAttach ( (ac.ac_va >> PAGESHIFT) + GSR_FIRSTLDTPAGE,
// LOTUS                                  pptda, ( pgLDTva>>PAGESHIFT), pptda, 6,
                                  pptda, ( pgLDTva>>PAGESHIFT), pptda, GSR_NUMLDTPAGES,
                                  PG_W |PG_R|PG_U|PG_REMAP) ) != NO_ERROR) )

            {

// GSR end
            VMFreeMem(ac.ac_va, hptda, NULL);
        } else  {
            pptda->ptda_ldthandle = ac.ac_hob;
            pptda->ptda_ldtaddr = (ulong_t) ac.ac_va;
// GSR start
            pptda->ptda_ldtpgmap = SEL_LDTPGMAPINIT;

            if (pptda->ptda_ProcType != PT_VDM) {
// GSR end
                /*
                 * Map selector 7 to the LDT
                 *
                 * BUGBUG - Until IanB starts the private arena at 64k,
                 *          we have to skip this for sysinit, since we
                 *          actually try to allocate something at sel 7.
                 */
                if (pptda->ptda_Pid != 0) {
                    SELSetDescInfo(SELSD_ALL,
                               D_PRES| D_SEG | D_DPL3 | D_DATA | D_R | D_UVIRT,
                               USERLDTSEL,
                               _64K,
                               ac.ac_va,
                               ac.ac_hob,
                               hptda,
                               0);
                }
            }
            *pladdr = (ulong_t) ac.ac_va;
        }
    }
    return(rc);
}

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

/***LP  selInitInfoSegLDT - Initialize task's InfoSeg
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   hptda   - context handle
 *      RETURN  int     - error code (NO_ERROR if no error)
 *
 *      PseudoCode:
 */
int
selInitInfoSegLDT(VMHOB hptda)
{
    int     rc = NO_ERROR;
    extern struct infoseg_s Sis2;
    extern struct infoseg_s Lis2;

    /*
     * Attach to infosegs after they have been allocated.  The sysinit
     * process performs the allocation, and every other process performs
     * an attach.
     */
    if (Sis2.is_hob != HOBNULL) {
        if ((rc = VMAttach(Sis2.is_hob, hptda, 0)) ==
                NO_ERROR) {
                    rc = VMAttach(Lis2.is_hob, hptda, 0);
        }
    }
    return(rc);
}


/***EP  SELInitLDT - Initialize LDT and task's InfoSeg
 *
 *      SELInitLDT initializes task's LDT and its InfoSeg
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY       hptda   - Context
 *      RETURN      int     - error code (NO_ERROR if no error)
 *
 *      PseudoCode:
 *
 *      Call selInitLDTSub
 *      Call selInitInfoSegLDT
 *      return
 */
int     SELInitLDT(hptda)
VMHOB   hptda;
{
    ulong_t laddr;                  /* address to put LDT address */
    int     rc, rc2;

    MMPHPreSELInitLDT()             /* Performance Hook (20/27) */

    if ((rc = selInitLDTSub(hptda, SSToDS(&laddr))) != NO_ERROR)
        return(rc);
    if ((rc = selInitInfoSegLDT(hptda)) != NO_ERROR) {
        while((rc2 = VMFreeMem(laddr, HOBNULL, NULL)) == ERROR_INTERRUPT);
        selAssert(rc2 == NO_ERROR);
    }

    MMPHPostSELInitLDT()            /* Performance Hook (20/A7) */
    return(rc);
}

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

/***EP  SELFreeTaskLDT - Free an entire LDT space.
 *
 *      If free flag is set, it frees the ldt; if shrink flag
 *      is set, it only leaves one page intact, the page containing
 *      infoseg selectors.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   hptda - Context handle.
 *              fs - FreeFlag (=1 => shrink ldt / =0 => free entire ldt)
 *      RETURN    int   - error code (NO_ERROR if success)
 *
 *      SELFreeTaskLDT(VMHOB hptda, ulong fl)
 *
 *      PseudoCode:
 *
 *      Call VMGetPTDAAddr(context handle)
 *      If (shrink flag)
 *          Call VMGetSem(LDT handle)
 *          For every page that is valid in the LDT
 *              If it is not the page containing the INFOSEGs
 *                  Turn off corresponding bit in ldtpgbitmap
 *                  Call PGSetTypeLDT to convert pages into discardable
 *          Call VMClearSem
 *      Else
 *          Call VMFreeMem(LDTLinaddr, ptda_handle, NA)
 *      Endif
 *      return
 */
int         SELFreeTaskLDT(hptda, fl)
VMHOB       hptda;                          /* context handle */
ulong_t     fl;                             /* free flags */
{
    register int i;
    int     rc;                              /* return code */
    register struct  ptda_s  *pptda = VMGetPTDAAddr(hptda);

    selAssert ((VMHandleMap(pptda->ptda_ldthandle))->ob_hobowner
        == SELLDTOWNER);
    if (fl & SELFT_SHRINKLDT) {
        selAssert(pptda->ptda_ldthandle);
        if ((rc = VMGetSem(pptda->ptda_ldthandle, VMGS_TAKESEM, NULL))
            != NO_ERROR)  return(rc);
        for (i = 0 ; i < (SEL_64K >> PAGESHIFT) ; i++) {
            /* for every page in the LDT */
            if (selIsValidLDTpg(pptda, i)  &&
                (i != (SEL_TASKINFOSEG >> PAGESHIFT))) {
                /* if it is valid and not containing infoseg selectors */

                if ((rc = PGSetTypeLDT(((pptda->ptda_ldtaddr >> PAGESHIFT) + i),
                    pptda->ptda_ldthandle, i, SEL_PGLDT1)) != NO_ERROR)
                    break;          /* Break if error */
                pptda->ptda_ldtpgmap  &= ~(1 << i);
                /* Turn off valid bit corresponding to that page */
            }
        }
        VMClearSem(pptda->ptda_ldthandle);
    } else  {               /* it is a free request */
        rc = VMFreeMem(pptda->ptda_ldtaddr, hptda, NULL);
        if (rc == NO_ERROR) {
            pptda->ptda_ldthandle = 0;
            pptda->ptda_ldtaddr = 0;
            pptda->ptda_ldtpgmap = 0;
        }
    }
    return(rc);
}

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

/***LP  selReallocSub - do reallocation on behalf of w_AllocSeg, ReallocSeg, or
 *                      w_ReallocHuge
 *
 *      selReallocSub is called by w_AllocSeg, ReallocSeg, or w_Reallochuge
 *      to change the size of a memory object.
 *      WARNING: selReallocSub assumes that the semaphore to object is obtained
 *      The object must not have any short term locks,
 *      otherwise selReallocSub will internal error.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   cbnew       - requested size
 *              cbcur       - current size
 *              pod         - Address of object descriptor
 *      RETURN  int         - error code (NO_ERROR if no error)
 *
 *      selReallocSub(ulong_t cbnew, ulong_t cbcur, SEL sel,
 *          VMOI *poi, struct seldesc_s *pdesc, VMHOB hptda)
 *
 *      PseudoCode:
 *
 *      Read max size from arena record info
 *      If (cbnew > cbmax), return (ERROR_ACCESS_DENIED)
 *      If (cbnew == cbcur), return (NO_ERROR)
 *      Call VMSetMemSub
 *      return(error code from VMSetMemSub)
 */
// int     selReallocSub(cbnew, cbcur, poi)
// ulong_t     cbnew;                  /* Requested new size */
// ulong_t     cbcur;                  /* Current size */
// VMOI        *poi;                   /* Address to read object info */

int     selReallocSub(cbnew, cbcur, pod)
ulong_t     cbnew;                  /* Requested new size */
ulong_t     cbcur;                  /* Current size */
register VMOD *pod;                 /* Address to read object info */
{
               ulong_t     p;
               ulong_t     cb;
               ulong_t     fl;
//             VMOD        od;
//  register   VMOD        *pod = SSToDS(&od);

//  VMOiToOd(poi, pod);

    selAssert((pod->od_par)->ar_f & AR_SELMAP);  /* Must be tiled */

    if (VMIsAliasOd(*pod))
    {
        return(ERROR_ACCESS_DENIED);
    }

    if (cbnew > VMSizeOd(*pod))
    {
        return (ERROR_ACCESS_DENIED);
    }

    if (cbnew == cbcur)
    {
        return (NO_ERROR);
    }

    /* Set up VMSetMemSub parameters */

    if (cbnew > cbcur)
    {
        fl = VMS_COMMIT | VMS_SELMAP | VMGetObPermOd(pod);

        selAssert ((AR_READ >> VMAC_PSHIFT) & PG_R);
        selAssert ((AR_WRITE >> VMAC_PSHIFT) & PG_W);
        selAssert ((AR_EXEC >> VMAC_PSHIFT) & PG_X);
        selAssert ((AR_USER >> VMAC_PSHIFT) & PG_U);

        if ((pod->od_pob)->ob_fs & OB_SHARED)
        {
            fl |= VMAC_SHARED;
        }

        if ((pod->od_pob)->ob_fs & OB_RESIDENT)
        {
            fl |= PG_RESIDENT;
        }
        else
        {
            fl |= PG_SWAPPABLE;
        }

        if ((pod->od_pob)->ob_fs & OB_ZEROINIT)
        {
            fl |= PG_ZEROFILL;
        }

        p  = pod->od_laddr + cbcur;
        cb = cbnew - cbcur;
    }
    else
    {
        /*
         * shrink request
         */

        fl = (VMS_DECOMMIT | VMS_SELMAP | PG_SKIPFREE);
        p  = pod->od_laddr + cbnew;
        cb = cbcur - cbnew;
    }

    return(VMSetMemSub(fl, pod, p, cb, NULL, cbcur));
}

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

/***LP  selAllocPhys - Allocate Fixed Block of Physical Memory
 *
 *      selAllocPhys is called by AllocPhysDM to allocate a block of
 *      fixed memory
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY   cb  - size in bytes
 *              fl  - flag  (=0 above 1 MEG, =1 below 1 MEG)
 *              pphys - Pointer to location to retun physical address
 *              userCS - caller's CS (used for ownership tracking)
 *              userEIP - caller's EIP (used for ownership tracking)
 *      RETURN  int - Error code, NO_ERROR if no error
 *
 *      PseudoCode
 *
 *      Call VMAllocMem
 *      If (no error), call PGPageToPhys
 */
#define SAP_VMAL (VMAC_LOCANY | VMAC_ALIGNPAGE | VMAC_ARENASYS)
#define SAP_PGAL (PG_RESIDENT | PG_CONTIG)

int selAllocPhys(cb, fl, pphys, userCS, userEIP)
ulong_t     cb;                     /* size in bytes */
ulong_t     fl;                     /* flags */
ulong_t     *pphys;                 /* location to return physical address */
SEL         userCS;                 /* DD callers CS  */
ulong_t     userEIP;                /* DD callers EIP */
    {
    int     rc;
    VMAC    ac;
    VMHOB   hobCaller;

    hobCaller = VMGetOwner(userCS, userEIP);

    if ((rc = VMAllocMem(cb, NULL, SAP_VMAL, HOBNULL, ALLOCPHYSOWNER,
        hobCaller, NULL, NULL, SSToDS(&ac))) == NO_ERROR) {
        if ((rc = PGAlloc(ac.ac_va >> PAGESHIFT, pPTDACur,
            (cb + PAGEMASK) >> PAGESHIFT, 0, ac.ac_hob, 0,
            fl ? (SAP_PGAL | PG_1M) : (SAP_PGAL | PG_16M))) != NO_ERROR) {
            VMFreeMem(ac.ac_va, HOBNULL, NULL);
        }
        else {
            rc = PGPageToPhys( (ac.ac_va >> PAGESHIFT), 0, pphys);
            selAssert(rc == NO_ERROR);
        }
    }
    return(rc);
}

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

/***LP  selFreePhys - Free physical memory allocated via DevHelp AllocPhys
 *
 *      selFreePhys frees physical memory allocated via DevHelp AllocPhys.
 *
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY       phys - Physical address
 *      EXIT        0 if success, 1 if mem was not allocated with AllocPhys
 *
 *      void selFreePhys(ulong phys)
 *
 *      PseudoCode
 *
 *      Call PGPhystoHOB
 *      if (error), return
 *      Call VMObjectInfo
 *      If (not DevHlp allocated object) panic
 *      Call VMFreeMem
 */
int selFreePhys(phys)
ulong_t phys;                  /* physical address */
{
    VMHOB   hob;
//  VMOI    oi;

    /* if the address is invalid or free or the hob is a system owner
     *   return error to caller
     */
//  if ((PGPhysToHOB(phys, SSToDS(&hob)) != NO_ERROR)  ||
//      (hob >= VMMINOWNER)) {
    if ((PGPhysToHOB(phys, SSToDS(&hob)) != NO_ERROR)  ||
            (vmHandleMap(hob)->ob_hobowner != ALLOCPHYSOWNER))
    {
        vmAssert(0);
        return(1);
    }

//  oi.oi_hob = hob;        /* Prepare for assertion checking below */
//  oi.oi_hobptda = HOBNULL;
//  if (VMGetObjectInfo(VMOI_HOB, SSToDS(&oi)) != NO_ERROR ||
//      oi.oi_hobowner != ALLOCPHYSOWNER) {
//      return(1);
//  }
//  VMFreeMemOb(hob, HOBNULL, NULL);
//  return(0);

    return(VMFreeMemOb(hob, HOBNULL, NULL));
}


/***EP  SELClearPreload - Clear the preload flag of the given descriptor
 *
 *      SELClearPreload clears the preload flag of the given descriptor
 *      by marking the descriptor present. It is called by the loader
 *      when swapping is disabled.
 *      WARNING: This procedure is in swappable code
 *
 *      ENTRY       sel - selector
 *                hptda - context
 *      RETURN    NONE
 *
 *      PseudoCode
 *
 *      Map context's LDT
 *      Turn on present flag in descriptor
 *
 */
void    SELClearPreload(sel, hptda)
SEL     sel;
VMHOB   hptda;
{
    struct desctab  *pdesc;

    pdesc = (struct desctab *) (selGetLDTAddr(hptda)) +
            (int) (sel >> SEL_SHIFT);
            /* pdesc points to descriptor */

    pdesc->d_access &=  ~D_ACCESSED;
    pdesc->d_access |= D_PRES;
}


#pragma code_seg()
#pragma code_seg("SWAP32_26", "CODE")
/***EP  SELCreateVdmLDT - allocate an LDT for a VDM and initialize its free list
 *
 *      This routine allocates an LDT object for a DPMI VDM process and
 *      initializes its free list.  The head of the free list is the first
 *      selector available after the few reserved for DMPI partying
 *      (SEL_DPMI_RESERVED).  The free list head points to a block of free
 *      selectors (pdescfree) which may link to further free
 *      selector blocks.
 *
 *      This routine must be called in the context of the VDM we are
 *      allocating the LDT for.
 *
 *      ENTRY:  pladdr - pointer to location to return a pointer to the new ldt
 *      EXIT:   NO_ERROR if success, else error code
 *
 *      Call selInitLDTSub to allocate ldt
 *      initialize free list
 */
#define SEL_DPMI_RESERVED   16          /* number of selectors that dpmi can
                                         * specifically allocate */
#define SEL_DPMI_HEAD   (SEL_DPMI_RESERVED * sizeof(struct descfree_s))
                                        /* selector of free list head */
int     SELCreateVdmLDT(pladdr)
void    **pladdr;
{
    register struct descfree_s *pdesc;
             int               rc;
             PTCB              pTCBtmp;

    selAssert(pPTDACur->ptda_ProcType == PT_VDM);
    if ((rc = selInitLDTSub(0, (ulong_t *)pladdr)) == NO_ERROR)
    {

        /*
         *  LDT is allocated, initialize free list.
         */
        pdesc = (struct descfree_s *)(*pladdr) + SEL_DPMI_RESERVED;
        pdesc->df_descNext = pdesc + 1;
        pdesc->df_cnt = 0;
        pdesc++;
        pdesc->df_descNext = NA;
        pdesc->df_cnt = ((PAGESIZE - SEL_DPMI_HEAD)/sizeof(struct desctab)) - 1;
    }

    /*
     *  Set up the ldt descriptor with the new ldt.
     *
     *  NOTE: when we put in  multiple LDT support, the code should be
     *  moved into the "swich ldt" routine.
     */
    // BUGBUG: hack here:  The following should change to call
    // a procedure to just set up the TCB area for the descriptor
    // for the LDT (28) and copy it to GDT 28.  Look carefully
    // at this code and correct it.  WayneCa 11/12/90

    /* If we have a multithreaded VDM, we need to update all threads */
    /* Skip over the first thread, and process all other threads     */
    if(pPTDACur->ptda_cTCB > 1)
    {
        pTCBtmp = pPTDACur->ptda_pTCBHead->TCBpTCBNext;
        while(pTCBtmp!=NULL)
        {
            PGKStackMap(pTCBtmp);
            pTCBtmp = pTCBtmp->TCBpTCBNext;
        }
    }

    PGKStackMap(pTCBCur);
    return(rc);
}

#pragma code_seg()
#pragma code_seg("SWAP32_28", "CODE")
/***EP  SELDestroyVdmLDT - free an LDT for a VDM                        //1.5
 *
 *      This routine destroys an LDT object for a DPMI VDM process and
 *      deallocates all of the memory associated with LDT.
 *
 *      This routine must be called in the context of the VDM we are
 *      freeing the LDT for.
 *
 *      ENTRY:  pladdr - pointer to location to return a pointer to the new ldt
 *      EXIT:   NO_ERROR if success, else error code
 *
 *      Call selFreeLDTSub to free ldt
 */
int     SELDestroyVdmLDT(pladdr)                                        //1.5
void    *pladdr;
{
    ULONG rc;
    selAssert(pPTDACur->ptda_ProcType == PT_VDM);

    if ((rc = VMFreeMem((ULONG)pladdr, 0, NULL)) == NO_ERROR) {
       pPTDACur->ptda_ldthandle = 0;                                    //61984
       pPTDACur->ptda_ldtaddr =   0;                                    //61984
       pPTDACur->ptda_ldtpgmap =  0;                                    //61984
    }

    return(rc);
}

#pragma code_seg()
#pragma code_seg("SWAP32_25", "CODE")
/***EP  SELSwitchVdmLDT - load new LDT for DPMI VDM task                //1.5
 *
 *      This routine switches the LDT that is currently being used for the
 *      DPMI task in the VDM.  The LDT must already exist before calling
 *      this routine.
 *
 *      This routine must be called in the context of the VDM we are
 *      switching the LDT for.
 *
 *      ENTRY:  pladdr - pointer to location to return a pointer to the new ldt
 *      EXIT:   NO_ERROR if success, else error code
 *
 *      Call selSwitchLDTsub to do LLDT
 *      switch kernel data structures
 */

extern void selSwitchLDTsub(void);                                      //1.5

//73188 VOID    SELSwitchVdmLDT(newldt)                                         //1.5
VOID    SELSwitchVdmLDT(newldt,curldt)                        //1.5, and 73188
register struct selswitchvdm_s *newldt, *curldt;                      // 73188
{
    register struct desctab *pDesc;
    PTCB pTCBtmp;

    /* If we have a multithreaded VDM, we need to update all threads */
    /* Skip over the first thread, and process all other threads     */
    if (pPTDACur->ptda_cTCB > 1) {
       pTCBtmp = pPTDACur->ptda_pTCBHead->TCBpTCBNext;
       while(pTCBtmp!=NULL) {
          pDesc = (struct desctab *) &(pTCBtmp->TCBpTSD->TSDdescLDT);
          pDesc-> d_loaddr  = (ushort_t) newldt->ssv_ldtaddr;
          pDesc-> d_hiaddr  = (unsigned char) (newldt->ssv_ldtaddr >> 16);
          pDesc-> d_extaddr = (unsigned char) (newldt->ssv_ldtaddr >> 24);
          pTCBtmp = pTCBtmp->TCBpTCBNext;
       }
    }

    pDesc = (struct desctab *) &(pTCBCur->TCBpTSD->TSDdescLDT);
    pDesc-> d_loaddr  = (ushort_t) newldt->ssv_ldtaddr;
    pDesc-> d_hiaddr  = (unsigned char) (newldt->ssv_ldtaddr >> 16);
    pDesc-> d_extaddr = (unsigned char) (newldt->ssv_ldtaddr >> 24);

    if (curldt) {                                                       //73188
       curldt->ssv_ldtpgmap = pPTDACur->ptda_ldtpgmap;                  //73188
    }                                                                   //73188

    pPTDACur->ptda_ldthandle = newldt->ssv_ldthandle;                   //61984
    pPTDACur->ptda_ldtaddr =   newldt->ssv_ldtaddr;                     //61984
    pPTDACur->ptda_ldtpgmap =  newldt->ssv_ldtpgmap;                    //61984

    selSwitchLDTsub();    // lldt();
}

#ifdef SELSTRICT
void SELPrintVdmLdt(void)
{
    register struct descfree_s *pdescFree;

    pdescFree = (struct descfree_s *) pPTDACur->ptda_ldtaddr +
                                SEL_DPMI_RESERVED;
    do
    {
        printf("%lx %x %lx %x\n", pdescFree,
                                  (ulong_t) pdescFree - pPTDACur->ptda_ldtaddr,
                                  pdescFree->df_descNext, pdescFree->df_cnt);
        pdescFree = pdescFree->df_descNext;
    }while(pdescFree != NA);
}
#endif

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

/***EP  SELAllocVdmSels - allocate consecutive LDT selectors for a DPMI VDM
 *
 *      This routine returns "csel" consecutive free selectors from
 *      the current process's LDT. If csel consecutive free selectors
 *      are not available and cselmin is not 0
 *
 *      The format of the free list is described in SELCreateVdmLDT().
 *
 *      ENTRY:  csel     - number of contiguous selectors to allocate.
 *              pcselmin - on entry pointer to minimum number of selectors to
 *                         allocate that will satisfy the request.
 *                         on return number of selectors allocated
 *              psel     - on entry: if *psel == -1, allocate any selector,
 *                            otherwise allocate that specific selector
 *      EXIT:   NO_ERROR if success, else error code
 *
 *      if not specific descriptor request
 *          walk LDT looking for enough consecutive selectors
 *          if not found
 *              grow ldt
 *      mark selectors in use
 */
int     SELAllocVdmSels(csel, pcselmin, psel)
ushort_t csel;
ushort_t *pcselmin;
ushort_t *psel;
{
    register struct descfree_s *pdescFree;
    register struct descfree_s *pdescPrev;
             struct descfree_s *pdescLimit;
    register struct desctab    *pdesc;
             struct descfree_s *pdescPrevLargest;
             int               rc;
             ushort_t          cselmin = *pcselmin;
             ushort_t          i;

    selAssert(pPTDACur->ptda_ProcType == PT_VDM);


    /*
     *  If the caller requested a specific selector just verify that they
     *  are all free.  Also don't let them specify my list head.
     */
    if (*psel != (ushort_t) -1)
    {
        pdescPrev = (struct descfree_s *) pPTDACur->ptda_ldtaddr +
                            SEL_DPMI_RESERVED;
        pdesc = (struct desctab *) pPTDACur->ptda_ldtaddr +
                                              (*psel >> SEL_SHIFT);
        selAssert(pdesc != (struct desctab *) pdescPrev);

        if(pdesc > (struct desctab *) pdescPrev)
        {
            pdescFree = pdescPrev->df_descNext;
            /*
             * commit pages in the LDT if needed
             */
            if((rc = selValidateLDT(pPTDACur, *psel, (ushort_t) csel)) !=
                                                                     NO_ERROR)
            {
                return(rc);
            }

            /*
             * find appropriate free block in the LDT
             */
            while(pdescFree != NA &&
                  (struct descfree_s *) pdesc >= pdescFree + pdescFree->df_cnt)
            {
                pdescPrev = pdescFree;
                pdescFree = pdescFree->df_descNext;
                selAssert(pdescFree == NA ||
                          pdescPrev + pdescPrev->df_cnt < pdescFree);
            }

            /*
             * insure requested block falls within free block
             *
             * If the descriptor is already allocated, it could be
             * that it is a GSR descriptor which was attached when
             * when the VDM's LDT was created. If so, deny access.
             * BUGBUG : should take the GSR descriptors off the free
             *          chain in SELCreateVdmLDT.                         150492
             */
            if(pdescFree == NA || pdesc < (struct desctab *) pdescFree ||
               pdesc + csel > (struct desctab *) pdescFree + pdescFree->df_cnt ||
               pdesc->d_access != 0)                         /* 150492 */

            {
                return(ERROR_ACCESS_DENIED);
            }

            /*
             *  remove requested block from free block
             */


            if(pdesc != (struct desctab *) pdescFree)
            {
                if((struct desctab *) pdescFree + pdescFree->df_cnt !=
                                                                 pdesc + csel)
                {
                    pdescPrev = (struct descfree_s *) pdesc + csel;
                    pdescPrev->df_descNext = pdescFree->df_descNext;
                    pdescPrev->df_cnt = (ushort_t) ((pdescFree +
                                            pdescFree->df_cnt) - pdescPrev);
                    pdescFree->df_descNext = pdescPrev;
                }
                pdescFree->df_cnt = (ushort_t) (pdesc -
                                                (struct desctab *) pdescFree);
            }
            else
            {
                if(pdescFree->df_cnt != (ushort_t) csel)
                {
                    pdescPrev->df_descNext = pdescFree + csel;
                    (pdescPrev->df_descNext)->df_cnt =
                                          pdescFree->df_cnt - (ushort_t) csel;
                    (pdescPrev->df_descNext)->df_descNext =
                                              pdescFree->df_descNext;
                }
                else
                {
                    pdescPrev->df_descNext = pdescFree->df_descNext;
                }

                ((struct desc32 *) pdescFree)->d32_low  = 0;
                ((struct desc32 *) pdescFree)->d32_high = 0;
            }
        }
        else
        {
            pdescFree = (struct descfree_s *) pdesc;
            for(i = 0; i < (ushort_t) csel; i++, pdescFree++)
            {
                if(((struct desc32 *) pdescFree)->d32_low != 0 ||
                   ((struct desc32 *) pdescFree)->d32_high != 0)
                {
                    return(ERROR_ACCESS_DENIED);
                }
            }
        }
    }
    else
    {
        /*
         *  A specific selector wasn't requested. Find free selectors
         *  to satisfy the request.
         */

        selAssert(cselmin <= csel);

        /*
         * To maintain compatibility with some ignorant windows programs
         * that expect all selectors to be positive values, will try to
         * allocate selectors that are positive first. Limit the inital
         * search for free selectors to the region of the LDT containing
         * positive selectors.
         */
        pdescLimit = (struct descfree_s *) (pPTDACur->ptda_ldtaddr +
                                                      (SEL_64K / 2));
        do
        {
            pdescPrev = (struct descfree_s *) pPTDACur->ptda_ldtaddr +
                                SEL_DPMI_RESERVED;
            pdescPrevLargest = pdescPrev;

            pdescFree = pdescPrev->df_descNext;

            if(pdescFree >= pdescLimit)
            {
                /*
                 * First free selector (lowest selector number available)
                 * is a negative value. If all positive pages in the LDT are
                 * valid then raise the limit. Otherwise force the next page
                 * in the LDT to become valid.
                 */
                if(selVdmAllPositiveValid(pPTDACur))
                {
                    pdescLimit = (struct descfree_s *) -1;
                }
                else
                {
                    pdescFree = NA;
                }
            }
            pdesc     = (struct desctab *) pdescFree;

            while(pdescFree != NA && pdescFree->df_cnt < csel)
            {
                if(((struct descfree_s *) pdesc)->df_cnt < pdescFree->df_cnt)
                {
                    pdesc = (struct desctab *) pdescFree;
                    pdescPrevLargest = pdescPrev;
                }
                pdescPrev = pdescFree;
                pdescFree = pdescFree->df_descNext;

                if(pdescFree > pdescLimit)
                {
                    /*
                     * selectors would now be negative. If already have found
                     * a block that would satisfy the request or there are more
                     * low pages to commit then exit loop. Otherwise the
                     * request cannot be satisfied with positive selectors
                     * so eliminate entry into this code.
                     */
                    if(((struct descfree_s *) pdesc)->df_cnt >= cselmin ||
                                         !selVdmAllPositiveValid(pPTDACur))
                    {
                        pdescFree = NA;
                    }
                    else
                    {
                        pdescLimit = (struct descfree_s *) -1;
                    }
                }
            }

            /*
             * At this point:
             * pdescFree != 0 - found a block of selectors that will satisfy
             *                  the request. uise this block
             *
             * pdescFree == 0 - Did not find a block to satisfy request. If
             *                  cselmin != csel then may have a block that
             *                  will satisfy the request. Otherwise a new
             *                  page in the LDT needs to be made valid.
             */

            if(pdescFree == NA)
            {
                if(pdesc != NA &&
                            ((struct descfree_s *) pdesc)->df_cnt >= cselmin)
                {
                    pdescFree = (struct descfree_s *) pdesc;
                    pdescPrev = pdescPrevLargest;
                    csel = pdescFree->df_cnt;
                }
                else
                {
                    for(i = 1; i < SEL_MAXPG && selIsValidLDTpg(pPTDACur, i);
                             i++);

                    rc = ERROR_NOT_ENOUGH_MEMORY;
                    if((i >= SEL_MAXPG) ||
                       ((rc = selValidateLDT(pPTDACur, i << PAGESHIFT, 1)) !=
                           NO_ERROR))
                    {
                        if(cselmin == csel || pdesc == NA)
                        {
                            return(rc);
                        }
                        pdescFree = (struct descfree_s *) pdesc;
                        pdescPrev = pdescPrevLargest;
                        csel = pdescFree->df_cnt;
                    }
                }
            }

        }while(pdescFree == NA);

        if(pdescFree->df_cnt == (ushort_t) csel)
        {
            pdescPrev->df_descNext = pdescFree->df_descNext;
        }
        else
        {
            pdescPrev->df_descNext = pdescFree + csel;
            (pdescPrev->df_descNext)->df_cnt = pdescFree->df_cnt -
                                                       (ushort_t) csel;
            (pdescPrev->df_descNext)->df_descNext = pdescFree->df_descNext;
        }
        ((struct desc32 *) pdescFree)->d32_low  = 0;
        ((struct desc32 *) pdescFree)->d32_high = 0;

        pdesc = (struct desctab *) pdescFree;
        *psel = (ushort_t) ((ulong_t) pdesc - pPTDACur->ptda_ldtaddr) |
                     TABLE_MASK | RPL_RING3;
        *pcselmin = csel;
    }
    /*
     *  We found the selectors to use. pdesc points to the 1st selector.
     *  Mark them all as allocated and return success.
     */
    while (csel-- > 0)
    {
        selAssert(((struct desc32 *)pdesc)->d32_low == 0 &&
                  ((struct desc32 *)pdesc)->d32_high == 0);
        pdesc++->d_access = D_SEG | D_DPL3;
    }

    return(NO_ERROR);
}


#pragma code_seg()
#pragma code_seg("SWAP32_24", "CODE")
/***EP  SELFreeVdmSels - free a group of DPMI LDT selector
 *
 *      This routine frees a group of selectors in the current process
 *      LDT, and coallesces them with other adjacent free selectors.
 *
 *      ENTRY:  sel - selector to free
 *              cnt - number of contiguous selectors to free
 *
 *      EXIT:   NO_ERROR if success, else error
 */
int     SELFreeVdmSels(ushort_t sel, ushort_t cnt)
{
    register struct descfree_s *pdesc;
    register struct descfree_s *pdescPrev;
    register struct descfree_s *pdescFree;
             ushort_t          i;

    selAssert(pPTDACur->ptda_ProcType == PT_VDM);

    pdescFree = (struct descfree_s *) pPTDACur->ptda_ldtaddr +
                    (sel >> SEL_SHIFT);
    pdescPrev = (struct descfree_s *) pPTDACur->ptda_ldtaddr +
                    SEL_DPMI_RESERVED;

    i = cnt & ~SEL_LDTVDMNOZERO;

    selAssert(pdescFree != pdescPrev);

    if(pdescFree > pdescPrev)
    {
        pdesc = pdescPrev->df_descNext;
        while(pdesc != NA && pdesc < pdescFree)
        {
            pdescPrev = pdesc;
            pdesc = pdesc->df_descNext;
        }

        if(pdescPrev + pdescPrev->df_cnt > pdescFree ||
               (pdesc != NA && pdescFree + i > pdesc))
        {
            return(ERROR_INVALID_ADDRESS);
        }
        pdescFree->df_descNext = pdesc;
        pdescFree->df_cnt = i;
        pdescFree->df_unused = 0;

        if(pdescFree + i == pdesc)
        {
            pdescFree->df_cnt += pdesc->df_cnt;
            pdescFree->df_descNext = pdesc->df_descNext;
            ((struct desc32 *) pdesc)->d32_low  = 0;
            ((struct desc32 *) pdesc)->d32_high = 0;
        }

        if(pdescPrev + pdescPrev->df_cnt == pdescFree)
        {
            pdescPrev->df_cnt += pdescFree->df_cnt;
            pdescPrev->df_descNext = pdescFree->df_descNext;
            ((struct desc32 *) pdescFree)->d32_low  = 0;
            ((struct desc32 *) pdescFree)->d32_high = 0;

        }
        else
        {
            pdescPrev->df_descNext = pdescFree;
            pdescFree++;
            i--;
        }
    }

    if((cnt & SEL_LDTVDMNOZERO) == 0)
    {
        while(i-- > 0)
        {
            ((struct desc32 *) pdescFree)->d32_low  = 0;
            ((struct desc32 *) pdescFree)->d32_high = 0;
            pdescFree++;
        }
    }
    return(NO_ERROR);
}
#pragma code_seg()
#pragma code_seg("SWAP32_26", "CODE")

/***EP  SELGetVdmInfo - return memory manager info for VDM use
 *
 *      This routine fills in the selvdminfo_s structure.
 *
 *      ENTRY:  psvi - pointer to selvdminfo_s struction to fill in
 *      EXIT:   none
 */
int SELGetVdmInfo(psvi)
struct selvdminfo_s *psvi;
{
    psvi->svi_linmax = MAXCRLADDR;
    psvi->svi_cphysranges = pgPageablePAI.pai_nranges;
    psvi->svi_physranges = (ulong_t *) pgPageablePAI.pai_pprt;
    return(NO_ERROR);
}

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

/***LP  selGetSeg - Get access to a sharable segment.
 *
 *      This function is called to allow the current task access to
 *      to a sharable segment that is already accessible to one or
 *      more other tasks.
 *
 *      int selGetSeg(SEL sel)
 *
 *      ENTRY           SEL             - Selector for sharable segment
 *      RETURN          int             - Result code (NO_ERROR = success)
 *
 *      PSEUDOCODE
 *
 *      Convert selector to laddr
 *      Call SELGetOd
 *      If (error), return (Error code)
 *      If (owner != getseg, or not shared segment, or selector not Ring3 data)
 *          return (ERROR_ACCESS_DENIED)
 *      If (already attached), return NO_ERROR
 *      Call VMAttach
 *      return (return code from VMAttach)
 */

int     selGetSeg(sel)
register SEL            sel;            /* Segment's selector */
{
  struct seldesc_s      seldesc;        /* Descriptor info */
  VMOD                  od;             /* Object info */
  int                   rc;             /* Result code */

  /*
   *  Reject request if a GDT selector was specified
   */

  rc = ERROR_ACCESS_DENIED;
  if(sel & SEL_LDTBIT)
  {
      /*
       *  Get the PTDA semaphore of any context attached to the shared arena
       *  shared content object.  Note that here we are checking static info
       *  on the object, (privilege, owner, etc...) so we don't have to take
       *  the object semaphore.  VMAttach later will verify that the object is
       *  still there.
       *
       */

      od.od_pPTDA = pPTDACur;
      if((rc = SELGetOd(VMOD_ANY_CTXT | VMOD_BASE | VMOD_PTDA_SEM |
                          VMGS_VERIFY, sel, SSToDS(&seldesc),
                          SSToDS(&od))) == NO_ERROR)
      {
          VMClearSem(VMHobPTDAOd(od));

          /*
           *  If the object is not in the shared arena, the given address
           *  parameter was invalid.  If the object does not have
           *  shareable data, deny access.
           */

          rc = ERROR_INVALID_PARAMETER;
          if(VMShrArenaOd(od))
          {
              /*
               *  Verify that the object has shared context and that the
               *  descriptor access byte indicates present ring 3 shared data
               *  and that the owner with the giveable flag masked off is
               *  GETSETOWNER
               */

              rc = ERROR_ACCESS_DENIED;
              if(VMShrContOd(od) &&
                ((seldesc.desc_acc & ~D_ACCESSED) == D_DATA3) &&
                ((VMOwnerOd(od) & ~(GIVESEGOWNER-SHRINDRSVD)) == GETSEGOWNER))
              {

                  /*
                   *  All is OK, Now call _VMAttach(hob, hptda,fl)
                   */

                  rc = VMAttach(VMHobOd(od), pPTDACur->ptda_handle,
                                    PG_U | PG_W | PG_R);
              }
          }
      }
  }

  /*
   *  Return result code.
   */

  if(rc == ERROR_INVALID_ADDRESS)
  {
      rc = ERROR_INVALID_PARAMETER;
  }

  return(rc);
}


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

/***LP  selGiveSeg - Give access to a sharable segment to another task.
 *
 *      This function is called to allow another task access to
 *      a sharable segment.
 *
 *      int selGiveSeg(ulong_t fl, SEL sel, VMHOB hobPTDA)
 *
 *      ENTRY           fl              - Flags
 *                                          SEL_HOBPTDA
 *                                            PTDA handle supplied
 *                                            If not given, a pid is assumed
 *                      SEL             - Selector
 *                      hobPTDA         - PTDA handle or pid
 *      RETURN          int             - Result code (no error if success)
 *
 *      PseudoCode:
 *
 *      Obtain Pid/hptda semaphore(keep task from terminating)
 *      Call SELObjectInfo(take semaphore)
 *      If (error), return (Error code)
 *      If (code sel || ring 0 data || not segment desc or not OB_SHARED)
 *          Call VMClearSem
 *          return (ERROR_ACCESS_DENIED)
 *      Endif
 *      If (owner != giveseg),
 *          Call VMClearSem
 *          return (ERROR_ACCESS_DENIED)
 *      Call VMAttach
 *      Call VMClearSem(hob)
 *      Call VMClearSem(hptda)
 *      return (return code from VMAttach)
 *
 */

int selGiveSeg(fl, sel, hobPTDA)
ulong_t                 fl;             /* Flags */
SEL                     sel;            /* Segment's selector */
VMHOB                   hobPTDA;        /* Target task PTDA handle or PID */
{
  VMOD                  od;             /* Object data */
  struct seldesc_s      seldesc;        /* Descriptor info */
  ptda_t                *pPTDA;         /* Pointer to target task's PTDA */
  ushort_t              uspid;          /* Process Id */
  VMHOB                 hob;            /* Object handle */
  int                   rc;             /* Result code */

  /*
   *  If Pid given, get PTDA address
   */

  uspid = (ushort_t) hobPTDA;
  rc = ERROR_ACCESS_DENIED;
  if(fl & SEL_GS_HOBPTDA)
    {
      uspid = (pPTDA->ptda_infoseg).LIS_CurProcID;
    }

  /*
   *  Verify that pid is non-zero and that an LDT selector has been
   *  specified.
   */

  if((uspid != HOBNULL) && (sel & SEL_LDTBIT))
    {
      /*
       *  Take the PTDA semaphore to prevent the task from terminating.  The
       *  TKPidGetSem function does that checking while getting the PTDA
       *  semaphore.
       */

      if((rc = TKPidGetSem(uspid, SSToDS(&pPTDA))) == NO_ERROR)
        {
          hobPTDA = pPTDA->ptda_handle;

          /*
           *  We obtained the hptda semaphore, and address of ptda, Now see if
           *  the owning task has a valid LDT (memory is not gone, task not in
           *  termination).  This condition is an internal error.
           */

          selAssert(pPTDA->ptda_ldthandle != HOBNULL);

          /*
           *  Get object info for the giveable segment as mapped by the
           *  current (donor) context and take the handle semaphore.
           */

          od.od_pPTDA = pPTDACur;
          if((rc = SELGetOd(VMGS_VERIFY | VMGS_TAKESEM,
                             sel, SSToDS(&seldesc),
                             SSToDS(&od))) == NO_ERROR)
            {
              rc = ERROR_ACCESS_DENIED;
              hob = VMHobOd(od);

              /*
               *  Verify that the segment is Ring 3 shared data owned
               *  by GIVESEGOWNER.
               */

              if((((seldesc.desc_acc & ~D_ACCESSED) == D_DATA3) &&
                  VMShrContOd(od)) &&
                  ((VMHobOwnerOd(od) & ~(GETSEGOWNER - SHRINDRSVD)) ==
                  GIVESEGOWNER))
                {
                  /*
                   *  Check that it is base selector if huge
                   */

                  if(seldesc.desc_laddr == od.od_laddr)
                    {
                      /*
                       *  All is OK, Now attach object to target task.
                       */

                      rc = VMAttach(hob, hobPTDA, PG_U|PG_R|PG_W);
                    }
                }

              /*
               *  Release the object semaphore.
               */

              VMClearSem(hob);
            }

          /*
           *  Release the PTDA semaphore for the target task.
           */

          VMClearSem(hobPTDA);
        }
    }

  /*
   *  Return result code to caller (NO_ERROR, ERROR_ACCESS_DENIED or
   *  rc from called routines.
   */

  return(rc);
}
#pragma code_seg()
