#define SCCSID "@(#)ldrste.c    6.76 92/03/26"

/***    OS/2 Object and segment loading routines
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      The loading routines handle the loading of
 *      .EXE file format objects and segments.
 *
 */

// Enhancement:for LAN failure handling:
// defect 71932. Paul.
// Added other return codes for LAN failure handling. Virginia. #74145
// Added other return codes(SYS57-59) for LAN failure handling. Ramon.#75589
// If base address being requested is not in the shared arena, then remove
//   NOINTERNFIXUPS from the flags.  wdm  #77647
// wdm - 82895 - re-integrate lost fix 77647 after 78715 was added
// 82707 06/28/94 ChangeTeam - Added code to bypass VMGetLaddr call to
//                             avoid IPE when loading VDD from floppy.
// 112629 10 MAR 1995 Change Team - Change to ldrAllocObjects to return   
//                          error if an object table entry of size zero is  
//                          encountered.                                   
// 109965  03/10/95  Change Team - When decompressing application pages, do
//                                 not allow trap in kernel if application is
//                                 corrupt
//
//   137076  12/8/95  Change Team - change clean-up of nonresident names table
//                                  to be cleaned-up during garbage collection.
//
//   147968 5/11/96  RJM  -  Change FSD and DD system owners to use HMTEs
//**************************************************************************//
#define FNO_NUM FNO_LDRSTE_C

#define INCL_ERROR_H            /* for bseerr.h */
#define INCL_ERROR2_H           /* for bseerr.h */
#define INCL_TYPES
#define FOR_EXEHDR      1

#include <osconst.h>
#include <ostypes.h>
#include <basedef.h>
#include <fnomem.h>
#include <osmac.h>
#include <mi.h>
#include <bseerr.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <pgexport.h>
#include <tkexport.h>
#include <vmexport.h>
#include <selxport.h>
#include <seldesc.h>
#include <infoseg.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#include <newexe.h>
#include <exe386.h>
#include <ldrxport.h>
#include <ldrvars.h>
#include <vmowner.h>
#include <mvdmexp.h>
#include <decomp32.h>

#ifdef PERFVIEW
#include <pvwxport.h>
#include <perfvw.h>
#endif


extern void ldrFreeMTEFixups(ldrmte_t *pmte, int flag);  // PERF: d# 98488

/* Function prototypes */

void ldrSetVMflags( ldrmte_t *pmte,
                    ulong_t fl,
                    ulong_t *pfl,
                    ushort_t *pflsel );

int ldrRsrcAlloc( ulong_t typeid,
                  ulong_t nameid,
                  VMHOB hobmte,
                  ptda_t *pptda );

int ldrGetPages( ldrmte_t *pmte,
                 ushort_t sfn,
                 ldrpip_t *pip,
                 ldropm_t *popm,
                 ldrote_t *pote );

void ldrMakeDataCache( ldrste_t *pste,
                       ulong_t psrc,
                       ulong_t cb );

void ldrUseDataCache( VMHOB hobcache,
                      ulong_t pdst,
                      ulong_t off,
                      ulong_t cb );

int ldrAllocDD( ldrmte_t *pmte,
                ldrste_t *pste,
                ulong_t cbseg,
                int iseg );

int ldrGetHugeSel( ldrmte_t *pmte,
                   ldrste_t *pste,
                   int iseg,
                   ulong_t *psize,
                   ulong_t *pcnt );

int ldrProcessRsrcSeg( ldrmte_t *pmte,
                       VMHOB hobptda,
                       int iseg,
                       int fpreload );

void ldrCopyVDD( ulong_t pchtgt,
                 ulong_t pchsrc,
                 ulong_t cchcopy );

void ldrResetDD( ulong_t pchtgt,
                 ulong_t cchcopy );

int ldr32UnpackPage( ldrmte_t * pMTE,
                     char * pPage,
                     unsigned long FileOffset,
                     ushort_t Sfn,
                     unsigned long cbIn );

int ldr32UnpackPage2( ldrmte_t * pMTE,
                      char *   pPage,
                      unsigned long FileOffset,
                      ushort_t Sfn,
                      unsigned long cbIn );

extern ulong_t ldrEditProLog( ldret_t *pet,
                              ulong_t *pentry,
                              ldrmte_t *pmte,
                              ldrlv_t *plv );

extern UCHAR IsMiniIFS;


//  Beginning of swappable code
#pragma code_seg("SWAP32_14","CODE")




/***EP  LDRCheckSig - check if the loader should abort
 *
 *      Check the EXIT (TK_FF_EXIT) and SIGNALPENDING (TK_FF_SIG) flags
 *      of the parent. If either of these flags are ON, return error
 *
 *      ENTRY   none
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *      This procedure performs the following steps
 *
 *      - if (TK_FF_EXIT OR TK_FF_SIG) AND (TCBCheckedSig == 0)
 *      -   return(ERROR_INTERRUPT)
 *
 */

int LDRCheckSig()
{
    register UCHAR checkedsig;

    if ( pTCBCur->TCBForcedActions & (TK_FF_EXIT | TK_FF_SIG) )
    {
        checkedsig = pTCBCur->TCBCheckedSig;
        pTCBCur->TCBCheckedSig = TRUE;
        if (checkedsig == FALSE)
            return(ERROR_INTERRUPT);
    }
    return(NO_ERROR);
}


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

/***LP  ldrAllocSegments - allocate segments/selectors for requested mte.
 *
 *      Each segment in the module which requests to be
 *      preloaded is allocated and all selectors for the
 *      module are allocated for the child task.
 *
 *      ENTRY   plv             - pointer to set of local variables
 *
 *      EXIT    None
 *
 *
 *   This procedure performs the following steps:
 *
 *      - For each segment in this module
 *      -   check if load should abort
 *      -   allocate memory
 */

int ldrAllocSegments( register ldrlv_t *plv )  // pointer to local variables
{
    register ldrmte_t  *pmte;     // pointer to mte
    register ldrsmte_t *psmte;    // pointer to swappable mte
    register ldrste_t  *pste;     // pointer to a segment table entry
    uchar_t            *pbcache;  // pointer to cache
    ULONG               laddr;    // place to store return linear addr
    VMAC                ac;       // VMAllocMem I/O packet
    ulong_t             i;
    int                 rc;
    ptda_t             *pPTDA;

    /* KDH - Update the PackShrSel...*/
    pPTDA = TKContext();
    pPTDA->ptda_infoseg.LIS_PackShrSel = SelShrPackedStart;
    pPTDA->ptda_infoseg.LIS_PackPckSel = SelShrPackedEnd;

    pmte = plv->lv_pmte;
    psmte = pmte->mte_swapmte;

    /*
     * KDH - Check for segment packing.  Segment packing is when we pack
     * together the code segments of a EXE into one large object and
     * reassign the selectors from their 64k tiled addresses.
     * First we will reserve an object the size of all the segments we are
     * going to pack.
     */
    if (psmte->smte_csegpack > 0)
    {
        if (!(pmte->mte_flags2 & MTEPACKSEGDONE))
        {
            if (plv->lv_type == EXT_PROGRAM)
            {
                if (rc = VMAllocMem(
                                                 /* virtual size */
                                  psmte->smte_csegpack * _64K,
                                                 /* commit size */
                                  psmte->smte_ssegpack,
                                                 /* PG & VM flags */
                                  PG_X | PG_U | PG_R | PG_SWAPPABLE |
                                  VMAC_ARENAPRV | VMAC_ALIGNSEL | VMAC_SELALLOC | VMAC_SHARED,
                                                 /* Context */
                                  pPTDA->ptda_handle,
                                                 /* Owner */
                                  (VMHOB) pmte->mte_handle,
                                                 /* MTE handle */
                                  (VMHOB) pmte->mte_handle,
                                                 /* Selector flags */
                                  SELAL_CODE | SEL_RPL3 << SELAL_PRIVSHIFT,
                                                 /* Block number */
                                  0,
                                                 /* return struct */
                                  SSToDS(&ac) ) )
                {
                    load_error( rc, pmte );
                    return(rc);
                }

            } // end of if type is EXT_PROGRAM


            else if (plv->lv_type == EXT_LIBRARY)
            {
                ldrAssert(VMPackedMem);

                ac.ac_va = vmPackedLBDRY;

                rc = VMAllocMem(
                                            /* virtual size */
                             psmte->smte_csegpack * _64K,
                                            /* commit size */
                             psmte->smte_ssegpack,
                                            /* PG & VM flags */
                             PG_X | PG_U | PG_R | PG_SWAPPABLE |
                             VMAC_ARENASHR | VMAC_ALIGNSEL | VMAC_LOCABOVE |
                             VMAC_SELALLOC | VMAC_SHARED | VMAC_PACKSHR,
                                            /* Context */
                             pPTDA->ptda_handle,
                                            /* Owner */
                             (VMHOB) pmte->mte_handle,
                                            /* MTE handle */
                             (VMHOB) pmte->mte_handle,
                                            /* Selector flags */
                             SELAL_CODE | SEL_RPL3 << SELAL_PRIVSHIFT,
                                            /* Block number */
                             0,
                                            /* return struct */
                             SSToDS(&ac));

                /* KDH - If we cannot allocate a virtual memory object in
                 *       the PACKSHR region, reset all packing parameters
                 *       so we can load them in the tiled region. */

                if ( (rc == ERROR_NOT_ENOUGH_MEMORY) || (rc == ERROR_BUSY) )
                {
                    for ( i = 1, pste = (ldrste_t *)psmte->smte_objtab;
                          i <= psmte->smte_objcnt;
                          i++, pste++ )
                    {
                        pste->ste_flags &= ~STE_PACKED;
                    }
                    psmte->smte_csegpack = 0;
                    psmte->smte_ssegpack = psmte->smte_spaddr = 0;
                    goto seg_pack_exit;
                }
                else if (rc)
                {
                    load_error(rc, pmte);
                    return(rc);
                }

            } // end of type is EXT_LIBRARY

            // save base address
            psmte->smte_ssegpack = psmte->smte_spaddr = ac.ac_va;

            // Save the selector and handle for each segment we are packing
            for ( i = 1, pste = (ldrste_t *)psmte->smte_objtab;
                  i <= psmte->smte_objcnt;
                  i++, pste++ )
            {
                if (pste->ste_flags & STE_PACKED)
                {
                    pste->ste_selector = (SEL) ac.ac_sel;
                    pste->ste_seghdl = ac.ac_hob;
                    ac.ac_sel += HUGEINCR;
                }
            }

        }  // end of allocation of segment packed objects

        // Save the first selector that is not packed in the private arena.
        if (plv->lv_type == EXT_PROGRAM)
        {
            for ( i = 1, pste = (ldrste_t *)psmte->smte_objtab;
                  i <= psmte->smte_objcnt;
                  i++, pste++ )
            {
                if (pste->ste_flags & STE_PACKED)
                {
                    pPTDA->ptda_infoseg.LIS_PackSel = pste->ste_selector;
                }
            }
            pPTDA->ptda_infoseg.LIS_PackSel += HUGEINCR;
        }

    } // end of special case segment packing code

seg_pack_exit:

    // Loop around to allocate each segment
    for ( plv->lv_objnum = 1, pste = (ldrste_t *) psmte->smte_objtab;
          plv->lv_objnum <= psmte->smte_objcnt;
          plv->lv_objnum++, pste++ )
    {

        if ( rc = LDRCheckSig() )
        {
            load_error( rc, pmte );
            return(rc);
        }

        if ( rc = ldrAllocSegment( pmte, pste, plv->lv_objnum, 0 ) )
        {

            /*
             * We got an error allocating the segments for this
             * module.  We must cleanup any segments that were
             * allocated before the error if the module we are loading
             * was a library module.  The reason is that the mte for this
             * module will not have a run time attachment to this process.
             * The case we are covering here is if a module that references
             * another, is not loaded that module's module pointer table
             * will not get the pointer to the mte for this one.  Therefore
             * when garbage collection is run this module will be freed
             * and the allocated segments will not get freed.
             */

            for ( plv->lv_objnum = 1, pste = (ldrste_t *) psmte->smte_objtab;
                  plv->lv_objnum <= psmte->smte_objcnt;
                  plv->lv_objnum++, pste++)
            {
                /*
                 * Skip packed segments, free as one object later
                 */
                if (pste->ste_flags & STE_PACKED)
                    continue;

                if (    (pste->ste_seghdl)
                     && (pste->ste_selector & SEL_LDTBIT)
                     && ((pste->ste_flags & STE_SHARED) == 0) )
                {
                    /* If cached instance data object */

                    ldrAssert( pste->ste_offset );

                    if ( VMHandleVerify( pste->ste_seghdl,
                                         1,
                                         SSToDS(&pbcache) ) == NO_ERROR )
                    {
                        // Free the pseudohandle
                        VMFreePseudoHandle( pste->ste_seghdl );

                        // Free small caches from heap
                        VMFreeKHB( VM_HKH_PUB_SWAPRW, pbcache );
                    }
                    else
                    {
                        // else cache has own object

                        if ( !(VMGetLaddr( pste->ste_seghdl,
                                           SSToDS(&laddr) ) ))
                            VMFreeMem( laddr, pPTDA->ptda_handle, 0 );
                    }

                    // Clear the handle
                    pste->ste_seghdl = HOBNULL;

                }
                else if (pste->ste_selector)
                {
                    // ok to use tiled - we know that it is not packed
                    VMFreeMem( SelToLaTiled( pste->ste_selector ),
                               pPTDA->ptda_handle,
                               VMFM_NOLDR | VMFM_GC );
                }

            }

            /*
             * See if any packed segments exists, free as one object.
             */
            if (    ( !(pmte->mte_flags2 & MTEPACKSEGDONE) )
                 && (psmte->smte_csegpack > 0) )
            {
#ifdef LDRSTRICT
                ldrAssert( !(VMFreeMem( psmte->smte_ssegpack, pPTDA->ptda_handle, NA )));
#else
                VMFreeMem( psmte->smte_ssegpack, pPTDA->ptda_handle, NA );
#endif
            }

            load_error( rc, pmte );
            return( rc );
        }

    } // end of loop to allocate each segment

    if (psmte->smte_csegpack > 0)
    {
        pmte->mte_flags2 |= MTEPACKSEGDONE;
    }

    return(NO_ERROR);

} // end of ldrAllocSegments

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


//@DB73642 - Add to reload segments that have been freed.
/***LP  ldrReloadSeg - Reload segment
 *
 *      ENTRY   pmte,pste,segno         - pointer to MTE,STE,segno to be reloaded
 *
 *      EXIT    none                    - goto load_error on error
 *
 *      COMMENT: Only preloads for 16-bit modules. 32-bit handled by
 *               ldrProcessObject. Only loads LDTs.
 */

int ldrReloadSeg( register ldrmte_t *pmte,
                  register ldrste_t *pste,
                  unsigned int segno )
{
    ldrlf_t                lf;
    ldrlv_t                lv;
    int                    rc;

    // We are going to call ldrSegment to preload this segment.
    // Initialise the lv and lf structures needed to make the call.

    memset( SSToDS(&lv), 0, (ulong_t)(sizeof(ldrlv_t)) );

    lv.lv_pmte = pmte;
    lv.lv_sfn = pmte->mte_sfn;
    lv.lv_hobmte = pmte->mte_handle;

    lf.lf_pallocbuf = lf.lf_objaddr = SELSelToLa( pste->ste_selector,
                                                  TKContext() );

    // Set the fault address to -1 so that the ldrSegment code
    // knows that it is not responding to a fault.
    lf.lf_faultaddr = -1;

    lf.lf_sallocbuf = pste->ste_selector;
    lf.lf_bno = (segno << 4);
    lf.lf_hMTE = pmte->mte_handle;

    // Do the reload
    if ( rc = ldrSegment( pste,
                          SSToDS(&lf),
                          SSToDS(&lv) ) )
    {
        load_error( rc, pmte );
        return( rc );
    }

    // Ensure that we only preload shared segments once
    pste->ste_flags &= ~STE_PRELOAD;
    return(NO_ERROR);

} // end of ldrReloadSeg
//@DB73642 - End of Insertion

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

/***LP  ldrAllocSegment - verify allocation size and allocate memory
 *
 *      If the segment's file size is larger than the minimum
 *      allocation size an error is reported.
 *      Both the allocation size and the segment handle are saved in
 *      the segment table entry. Also indicate the segment
 *      is being loaded so that it's memory will not be
 *      discarded during the load.
 *
 *      ldrAllocSegment (pmte, pste, iseg)
 *
 *      ENTRY   pmte            - pointer to mte
 *              pste            - pointer to ste
 *              iseg            - segment number
 *              fLoadRsrc       - true if called to allocate resource segment
 *      RETURN  int             - error code (NO_ERROR if successful)
 *
 *      - check if all objects must be preloaded
 *      - check for preload and shared objects
 *      - only preload shared objects once
 *      - check that privilege level only for ring 2 or ring 3
 *      - if object to be loaded in ring 2
 *      - if object size > object virtual size
 *      -   goto loaderror(error_invalid_min_alloc_size)
 *      - if mte flags = FSDMOD
 *      -   set access flags |= allocte GDT | don't RPL stamp GDT selector
 *      - else
 *      -   set access flags |= allocate LDT
 *      - if this is a library module
 *      -   set access flags |= shared selector
 *      - if object flags = shared
 *      -  set handle flags to shared
 *      - if object flags = ERONLY
 *      -   set access flags |= D_W
 *      - if object flags = code_type
 *      -   set access flags |= D_CODE
 *      - if object flags = CONFORMING
 *      -   set access flags |= D_CONFORM
 *      ` if (code_object or shared data object)
 *      -   set owner = mte handle
 *      -   if object allocated
 *      -     call ex_segattach()
 *      - else
 *      -   set owner = child_task's PTDA_handle
 *      -   if (object <= 4k && object read_only)
 *      -     set handle = object to discardable
 *      -   else if (MTE = DISCARD) && (code_object or data_read_only) &&
 *                  (no fixups present)
 *      -     set handle flags = object to discardable
 *      -   else
 *      -     set handle flags = swappable
 *      -   if mte = FSD module
 *      -     if (GetHugeSel() = 0)
 *      -       set access flags = allocated
 *      -   else
 *      -     call segalloc()
 *      -     clear RPL bits from selector and save in object
 *      - convert SEGDPL bits from object flags to RPL bits and place
 *      - in selector of object
 *      - if object flags != ring_3
 *      -   if object flags != movable
 *      -     goto loaderror(error_ring2seg_must_be_movable)
 *      - allocate a stack for ring 2 objects
 *      - allocate or attach to callgates for this object
 */

int ldrAllocSegment( register ldrmte_t *pmte,       // MTE pointer
                     register ldrste_t *pste,       // STE pointer
                     ulong_t            iseg,       // Segment number (1-based)
                     ulong_t            fLoadRsrc ) // True if called to allocate resource segment
{
    register ldrsmte_t *psmte;           // swappable mte pointer
    ulong_t             cbseg;           // Size of segment
    int                 frsrc;           // Resource segment flag
    VMHOB               hobowner;        // Owner i.d.
    VMHOB               hobptda;         // Context
    ldrste_t           *ptempste;        // temp pointer to ste for huge rsrc
    VMAC                ac;              // VMAllocMem I/O packet
    ulong_t             flsel =
                           SELAL_SELMAP; // Selector Manager flags
    ulong_t             rsrcsize = 0;    // huge resource size returned
    ulong_t             rsrccnt = 0;     // huge resource count returned
    int                 frsrcused = 0;   // True if resource really used
    ulong_t             flvm = 0;        // VM and Page Manager flags
    int                 rc = NO_ERROR;   // Return code

    memset( SSToDS(&ac), '\0', sizeof(VMAC) );

    psmte = pmte->mte_swapmte;

    // Resource segments are always "last". Use this to set the resource
    // flag if this is one of the "last" segments.
    frsrc = iseg > psmte->smte_objcnt - psmte->smte_rsrccnt;

    /*
     *  Note that for debugging purposes, we will actually allocate
     *  memory for resources that are not marked STE_PRELOAD if the
     *  fLoadRsrc flag is set.  Such resources are born with a
     *  reference count of zero instead of one, however.  The flag
     *  frsrcused is used to distinguish the special case.
     */
    if (    !(pste->ste_flags & STE_PRELOAD)
         && frsrc
         && !fLoadRsrc )
        return(NO_ERROR);               /* Skip non-preloaded resources */

    /*
     *  If discarding of segments is not allowed, for example, because the
     *  load media is removable, we need to ensure that a segment cannot be
     *  in the discarded state.  Marking the segments as non-discardable
     *  (which we do later in this routine) is not sufficient, since a
     *  LOADONCALL non-discardable segment may initially be in the discarded
     *  state.  We must also force preloading of segments.  For a shared
     *  segment, we force preloading the first time the module is loaded;
     *  for a non-shared segment, we force preloading on a per-instance
     *  basis, i.e. each time a process attaches to the segment.
     */

    //     if swapping off or preload segment
    // and if shared object
    if (    ( !PGSwapEnabled || (pste->ste_flags & STE_PRELOAD) )
         && (pste->ste_flags & STE_SHARED) )
    {
            frsrcused = TRUE;           /* Resource actually referenced */
    }

    /*
     *  Check the privilege level specified for the segment and set
     *  the privilege level in flaccess, allowing only levels 2 & 3.
     */

    if ( IsMiniIFS )
    {
        // Don't mark mini-IFS preload
        pste->ste_flags &= ~STE_PRELOAD;

        flsel |= ( pste->ste_flags & STE_DATA ? SEL_RPL3 << SELAL_PRIVSHIFT
                                              : SELAL_CONFORM );
        flvm |= PG_U;
    }

    else if ( (pste->ste_flags & STE_SEGDPL) == STE_RING_2 )
    {
        // it is an IOPL segment
#if 0
        // Error if invalid IOPL module
        if ( !(pmte->mte_flags1 & MTEIOPLALLOWED) )
            return(ERROR_IOPL_NOT_ENABLED);
#endif

        // Remember IOPL
        flsel |= SEL_RPL2 << SELAL_PRIVSHIFT;
    }

    else if ( (pste->ste_flags & STE_SEGDPL) == STE_RING_3 )
    {
        // it is a ring 3 segment

        /* Normal user segment */
        flsel |= SEL_RPL3 << SELAL_PRIVSHIFT;

        flvm |= PG_U;                   /* Set user level bit */
    }

    else
    {
        return(ERROR_INVALID_SEGDPL);  /* Invalid privilege level */
    }

    // segment size of zero means allocate 64K
    if ( (cbseg = pste->ste_minsiz) == 0 )
        cbseg = _64K;

    if (     ( pmte->mte_flags1 & FSDMOD && !IsMiniIFS )
         ||  (pste->ste_flags & STE_GDTSEG) )
    {
        /* FSD or permanent DD segment */

        /*
         *  For FSDs, set the segment table entry flags to STE_GDTSEG
         */
        pste->ste_flags |= STE_GDTSEG;

        /*
         *  This is a permanent device driver segment or an FSD
         *  segment.  Allocate it from the kernel resident heap.
         */
        return( ldrAllocDD( pmte, pste, cbseg, iseg ) );
    }

    if ( (pmte->mte_flags1 & LIBRARYMOD) || frsrc )
    {
        if (IsMiniIFS)
            // Mini FSD in system arena
            flvm |= VMAC_ARENASYS | VMAC_ALIGNPAGE | VMAC_SELALLOC;
        else
            // DLLs and resources in shared arena
            flvm |= VMAC_ARENASHR | VMAC_ALIGNSEL | VMAC_SELALLOC;
    }
    else
        // Others in private arena
        flvm |= VMAC_ARENAPRV | VMAC_ALIGNSEL | VMAC_SELALLOC;

    if (!frsrc)
        flvm |= VMAC_RETAIN;            /* Retain address space if not rsrc */

    if ((pste->ste_flags & STE_SHARED) && !IsMiniIFS) /* If memory is shared */
    {
        flvm |= VMAC_SHARED;            /* Allocate shared memory */

        /*
         *  Protected memory is allocated for shared, writable data
         *  segments in protected DLLs.
         */
//      if (    (pste->ste_flags & (STE_TYPE_MASK | STE_ERONLY)) == STE_DATA
//           && (pmte->mte_flags1 & (MTEMODPROT|LIBRARYMOD))
//                                                 == (MTEMODPROT|LIBRARYMOD) )
//          flvm |= VMAC_PROT;          /* Check if protected memory needed */
    }

    if ( (pste->ste_flags & STE_TYPE_MASK) == STE_CODE )
    {
        // it is a code segment

        flvm |= PG_X;         // Give execute permission
        flsel |= SELAL_CODE;

        // not execute-only
        if ( !(pste->ste_flags & STE_ERONLY) )
        {
            flsel |= SELAL_R;  // Make code readable
            flvm |= PG_R;
        }

        // conforming code
        if (pste->ste_flags & STE_CONFORM)
        {
            flsel |= SELAL_CONFORM;  // Make selector conforming
            flvm |= PG_U;            // Allow ring 3 page access
        }

    }
    else if ( (pste->ste_flags & STE_TYPE_MASK) == STE_DATA )
    {
        // it is a data segment

        flvm |= PG_R; // Give read permission

        // If not read-only
        if (!(pste->ste_flags & STE_ERONLY))
        {
            flsel |= SELAL_W; // Make data writable
            flvm |= PG_W;
        }

    }

    // Get PTDA handle
    if ( (hobptda = pPTDACur->ptda_child) == HOBNULL )
        hobptda = pPTDACur->ptda_handle;

    // Set the owner to the MTE for shared segments; for private segments
    // set the owner to be the PTDA handle.
    hobowner = hobptda;
//147968    if (flvm & VMAC_SHARED)
    if ((flvm & VMAC_SHARED) || IsMiniIFS) //147968
        hobowner = pmte->mte_handle;

//147968    // EXCEPT, set mini-fsd to be owned by first FSD owner
//147968    if (IsMiniIFS)
//147968        hobowner = LDRFSDOwner;

    if ( (flvm & VMAC_SHARED) && pste->ste_seghdl != HOBNULL )
    {
        // shared segment

        // Check for packed segment and first load of the EXE.  We need to
        // do a VMSetMem commit on the segment.
        if (    pste->ste_flags & STE_PACKED
             && (!(pmte->mte_flags2 & MTEPACKSEGDONE))
             && psmte->smte_csegpack > 0 )
        {
            SELSetDescInfo( SELSD_ALL,
                            (ushort_t) ( D_CODE | D_SEG | (D_RX |
                                 (((pste->ste_flags & STE_SEGDPL) == STE_RING_2) ?
                                 ((pste->ste_flags & STE_CONFORM) ?
                                 (D_DPL2 | D_CONFORM) : D_DPL2) : D_DPL3))),
                            pste->ste_selector,
                            pste->ste_minsiz,
                            psmte->smte_spaddr,
                            pste->ste_seghdl,
                            hobptda,
                            0 );
            psmte->smte_spaddr += pste->ste_minsiz;
            ac.ac_sel = (SEL) pste->ste_selector;
            rc = NO_ERROR;
        }

        else  // not packed
        {
            // see if already attached
            rc = VMIsAttached( pste->ste_seghdl,
                               hobptda );

            // all done for resources - they can not be IOPL
            if ( (!rc) && (frsrc) )
                return(NO_ERROR);

            if ( rc )
            {
                // if not attached, try to attach
                if ( rc = VMAttach( pste->ste_seghdl,
                                    hobptda,
                                    0 ) )
                    return(rc);
            }

            ac.ac_sel = pste->ste_selector; // Save selector
        }
    }

    else   // Else segment unallocated
    {
        // check to see if we already have a selector allocated
        if (pste->ste_selector != 0)
        {

            /*
             *  We could be processing a DosLoadModule call on a
             *  module to which we were attached already but one
             *  or more of whose segments we freed.  We don't want
             *  to allocate new copies of any instance data segments
             *  to which we are already attached.
             */
            if ( VMGetHandle( SELSelToLa( pste->ste_selector,
                                          TKContext() ),
                              hobptda,
                              SSToDS(&ac.ac_hob) ) == NO_ERROR )
                return(NO_ERROR);       /* Done if already attached */
        }

        if (IsMiniIFS)
        {
            // Low, resident, contiguous memory
            flvm |= PG_RESIDENT | PG_CONTIG | PG_1M;
            pste->ste_flags |= STE_GDTSEG;
        }
        /*  perf enh - since the swapper is much faster than the loader
         *             put the system dlls in the swap file
         */
        else  if (pmte->mte_flags1 & MTESWAPONLOAD)   // perf-enh
              flvm |= PG_SWAPONLOAD;                  // perf-enh

        else if ( !(pmte->mte_flags1 & MTE_MEDIAFIXED) )
            flvm |= PG_SWAPPABLE; // Swappable, AOD if no swapping

        // @DB72456: only if its not writable or iterated is it discardable
        // Iterated must be swappable ... we get into trouble trying to
        // page fault in part of an iterated segment otherwise.
        else if (!(flsel & SELAL_W) && !(pste->ste_flags & STE_ITERATED))
            flvm |= PG_DISCARDABLE; // Discard code and read-only data

        else
            flvm |= PG_SWAPONLOAD; // Data is swappable, discarded

        if (!(pmte->mte_flags1 & DEVDRVMOD))   // not device driver
        {
            if ( rc = ldrGetHugeSel( pmte,
                                     pste,
                                     iseg,
                                     SSToDS(&rsrcsize),
                                     SSToDS(&rsrccnt) ) )
            {
                return(rc);
            }

            if (pste->ste_selector != 0)
            {
                // The selector value is known

                // save selector
                ac.ac_sel = pste->ste_selector;

                // Compute virtual address
                // ac.ac_va = SELSelToLa( ac.ac_sel, TKContext() );  @d78715
                // Selectors are not actually allocated on a         @d78715
                // prereservation request so we can't get the        @d78715
                // virtual address from LDT.                         @d78715
                // See VMReserveMem in vmalloc.c                     @d78715
                ac.ac_va = SelToLaTiled( ac.ac_sel );              //@d78715

                // Use specific address
                flvm = (flvm & ~VMAC_LOCMASK) | VMAC_LOCSPECIFIC;

                /* If the selector resides in the shared arena
                 * we're requesting an allocation out of virtual memory
                 * reserved for reloading an MTE object. Specify
                 * VMAC_RELOAD so VMAllocMem does not return ERROR_BUSY.
                 */
                if (    VMShrArena(ac.ac_va)
                     && ( pmte->mte_flags1 & MTEPROCESSED ) )
                {
                    flvm |= VMAC_RELOAD;
                }

                if (pste->ste_flags & STE_SELALLOC)
                {
                    // Set flag if selector allocated
                    flsel |= SELAL_ALLOCATED;

                    // Turn flag off for next iteration
                    pste->ste_flags &= ~STE_SELALLOC;

                    // Indicate a reload to VM
                    flvm |= VMAC_RELOAD;
                }
            }
        }

        if ( rc = VMAllocMem( (rsrcsize == 0)
                                 ? _64K
                                 : rsrcsize,      /* Virtual size */
                              (rsrcsize == 0)
                                 ? cbseg
                                 : rsrcsize,      /* Committed size */
                              flvm,               /* VM and PG flags */
                              hobptda,            /* Context */
                              hobowner,           /* Owner */
                              pmte->mte_handle,   /* MTE handle */
                              flsel,              /* Selector Manager flags */
                              flvm & PG_RESIDENT
                                  ? 0
                                  : iseg << 4,    /* Block number */
                              SSToDS(&ac) ))      /* Virtual address */
        {
            return(rc);
        }

        //  Make sure to NOT set ste_seghdl for private LDT segments.
        //  In addition to not making sense, the scheme for caching
        //  DLL instance data segments requires ste_seghdl to be 0 for
        //  private LDT segments until it is used for a cache handle.
        if (!(ac.ac_sel & SEL_LDTBIT))  // if a GDT selector
        {
            pste->ste_seghdl = ac.ac_hob; // Save handle in STE

            // Save RPL 0 selector
            pste->ste_selector = (SEL)(ac.ac_sel & SEL_RPLCLR);

            return(NO_ERROR);
        }

        if (pste->ste_flags & STE_SHARED)
        {
            pste->ste_seghdl = ac.ac_hob;
        }
        else
        {
            if (!swapping || !(pmte->mte_flags1 & MTE_MEDIAFIXED))
                pste->ste_flags |= STE_PRELOAD;
        }

    }  // end else segment unallocated

    // Verify shift constant
    // ldrAssert( (STE_RING_3>>10) == SEL_RPL3 );

    // Stamp selector with DPL ????
    pste->ste_selector = (SEL)( (ac.ac_sel & SEL_RPLCLR) |
                                ((pste->ste_flags & STE_SEGDPL) >> 10) );

    /*
     * if this is a huge rsrc segment (rsrcsize != 0) stamp all the remaining
     * selector segment with DPL stamp.
     */
    if (rsrcsize != 0)
    {
        ptempste = &((ldrste_t *) psmte->smte_objtab)[iseg];
                                      /* point to next ste entry */
        rsrccnt--;                    /* the base selector processes already */
        while (rsrccnt-- > 0)
        {
            ptempste->ste_selector =
              (SEL)((ptempste->ste_selector & SEL_RPLCLR) |
              ((ptempste->ste_flags & STE_SEGDPL) >> 10));
            ptempste++;
        }
    }

    // Must not have null selector
    ldrAssert((pste->ste_selector & SEL_RPLCLR) != 0);

    if (    frsrc
         && ( rc = ldrProcessRsrcSeg( pmte,
                                      hobptda,
                                      iseg,
                                      frsrcused ) ) != NO_ERROR )
    {
        return(rc);
    }

    /* @DB73642 - if STE_PRELOAD set and floadrsrc set then the      */
    /*            resource was freed and is now being accessed       */
    /*            we must make sure it gets reloaded now.            */
    if (frsrc && fLoadRsrc && (pste->ste_flags & STE_PRELOAD))
      ldrReloadSeg(pmte,pste,iseg);
    /* @DB73642 - End of insertion                                   */

    // if ring 2 segment loaded
    if ( (pste->ste_flags & STE_SEGDPL) == STE_RING_2 )
    {

        fTKUsesR2 = TRUE;

        // We only want callgates for code segments.
        if (!(pste->ste_flags & STE_DATA))
        {
            // Allocate call gates
            rc = ldrEachObjEntry( iseg,
                                  pmte,
                                  ldrGetCallGate,
                                  0 );
        }

    }

    return(rc);

} // end of ldrAllocSegment

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

/***LP  ldrAllocObjects - allocate objects for requested mte.
 *
 *      For each object of this module call the memory manager to get a
 *      linear address reserved and request the overcommitment.
 *
 *      ENTRY   plv                     - pointer to local variables
 *
 *      EXIT                            - memory for objects allocated
 *
 *
 *   This procedure performs the following steps:
 *
 *      - For each object in this module
 *      -   check if loader should abort
 *      -   call ldrAllocObject
 *      - allocate or attach to call gates
 *
 *      In the following table the headers mean the following:
 *
 *      Force Preload:  When force preload is yes one of the following
 *      -------------   is true, the executable image is being loaded
 *                      from a removable media.  Or swapping has been
 *                      disabled.
 *
 *      Object type:    The object type is determined from the object
 *      -----------     flags found in the Object table entry. The
 *                      following types mean:
 *                      ro - readonly
 *                      rw - read/write
 *                      non-sparse - the object contains no invaild pages.
 *                      sparse - the object contains invalid pages.
 *
 *      Initial         The initial allocation type is the flags used in
 *      allocation      the VMAlloc call to allocate the memory for this
 *      type:           object.  The following are the types:
 *      ----------      reserve -
 *                      commit -
 *                      discarded - the initial state of the pages in the
 *                                  object are allocated as discarded.
 *                      discardable - the pages may be discarded
 *                      swappable - the pages have to be swapped
 *
 *      Page type       After the objects have been allocated the page table
 *      within object   entries are scanned.  Another VMAlloc call or
 *      -------------   VMSetType may be needed to convert pages to a different
 *                      type, the following are the types:
 *                      valid - this is a valid page in the object.
 *                      invalid - this is a invalid page in the object.
 *                      zero fill - this page initialized with zeros.
 *
 *      Per-page        These are the same as in initial allocation type.
 *      state change
 *      ------------
 *
 * Memory allocation Table
 *
 * Force    Object type     Initial allocation     Page type      Per-page
 * Preload                  type                   within object  state change
 * -------  --------------  ---------------------  -------------  ------------
 * no       ro  non-sparse  reserve+commit+
 *                          discarded+discardable
 *                                                 valid          none
 *                                                 zero fill      *not allowed*
 * no       ro  sparse      reserve
 *                                                 valid          commit+discrd
 *                                                                discardable
 *                                                 zero fill      *not allowed*
 *                                                 invalid        none
 * no       rw  non-sparse  reserve+commit+
 *                          discarded+swappable
 *                                                 valid          none
 *                                                 zero fill      swap+zfill
 * no       rw  sparse      reserve
 *                                                 valid          commit+discrd+
 *                                                                swappable
 *                                                 zfill          commit+swap+
 *                                                                zfill
 *                                                 invalid        none
 * yes      ro  non-sparse  reserve+commit+
 *                          discarded+swappable
 *                                                 valid          none
 *                                                 zero fill      *not allowed*
 * yes      ro  sparse      reserve
 *                                                 valid          commit+discrd+
 *                                                                swappable
 *                                                 zero fill      *not allowed*
 *                                                 invalid        none
 * yes      rw  non-sparse  reserve+commit+
 *                          discarded+swappable
 *                                                 valid          none
 *                                                 zero fill      swap+zfill
 * yes      rw  sparse      reserve
 *                                                 valid          commit+discrd+
 *                                                                swappable
 *                                                 zfill          commit+
 *                                                                swap+zfill
 *                                                 invalid        none
 *
 */

int ldrAllocObjects( ldrlv_t * plv )
{
    ptda_t            *pptda;      /* Current or ExecChild ptda    */
    register ldrmte_t *pmte;       /* pointer to a module table entry */
    ldrsmte_t         *psmte;      /* pointer to swappable mte */
    register ldrote_t *pote;       /* pointer to a object table entry */
    ULONG              lobjnum;
    ULONG              lobjnum2;
    ULONG              laddr;
    int                rc;
#ifdef  LDRSTRICT
    char modname[9];
#endif

    pptda = TKContext();            /* Set context */

    pmte = plv->lv_pmte;            /* point to mte for object to alloc */
    psmte = pmte->mte_swapmte;

    for ( lobjnum = 1; lobjnum <= psmte->smte_objcnt; lobjnum++ )
    {

        // check to see if we should abort
        if ( rc = LDRCheckSig() )
        {
            load_error(rc, pmte);
            return( rc );
        }

        // If it is a DLL, we need to reverse the order to get ascending
        // base addresses.
        if ( plv->lv_type == EXT_LIBRARY )
        {
            // Point to object table entry for the object we are going to alloc
            pote = ((ldrote_t *)psmte->smte_objtab)
                                            + ( psmte->smte_objcnt - lobjnum );
                                                   // wdm 82895 (77647) START:
            if (pmte->mte_flags1 & NOINTERNFIXUPS) // linker suppplied address
            {
              // Defect 82895/77647 was a bad fix.  As the comments in
              // the header for VMShrArena macro (VMEXPORT.H), VMShrArena
              // checks to see if the address lies between the minimum and
              // maximum addresses already RESERVED in the shared arean.
              // This is not what we want for R207.  We want to make sure the
              // address is above the minumum allowed basing address.
              // For R207, this is MINRWBASINGLADDR.
              //
              // Address below minimum allowed base address (and not resource)?
                 if (!(pote->ote_flags & OBJRSRC) &&     // not resource
                     (pote->ote_base < MINRWBASINGLADDR))
                    // Yes, so we must reapply the internal fixups
                    pmte->mte_flags1 &= ~NOINTERNFIXUPS;
            }                                     // wdm 82895 (77647) END
        }
        else // Not a DLL
        {
            // Point to object table entry for the object we are going to alloc
            pote = ((ldrote_t *)psmte->smte_objtab) + ( lobjnum - 1 );
        }

        /* -- check for object size of zero -- */                   //112629
        if (pote->ote_size > 0) {                                   //112629
           /* continue */                                           //112629
        }                                                           //112629
        else {                                                      //112629
            /* -- cannot allocate an object of size zero */         //112629
            rc = ERROR_BAD_EXE_FORMAT;                              //112629
            ldrStop(0, pmte);                                       //112629
            load_error(rc, pmte);                                   //112629
            return( rc );                                           //112629
        }                                                           //112629

        // Allocate objects.
        // Old way - only allocate resource objects if marked preload :
        // if ( pote->ote_flags & OBJPRELOAD || !( pote->ote_flags & OBJRSRC ) )
        // New way - only allocate resource objects if removeable media
        if (    !(pote->ote_flags & OBJRSRC)            // not resource
             || !(pmte->mte_flags1 & MTE_MEDIAFIXED) )  // removeable
        {
            if ( rc = ldrAllocObject( pmte,
                                      pote,
                                      plv->lv_type,
                                      pptda,
                                      ( plv->lv_type == EXT_LIBRARY )
                                          ? psmte->smte_objcnt - lobjnum + 1
                                          : lobjnum
                                    ) )
            {
              /*
               * We got an error allocating the object for this module
               * It was either due to the pre-relocated addresses or
               * some other problem.
               */

              if (pmte->mte_flags1 & NOINTERNFIXUPS)
              { /* Error probably occurred due to something already allocated
                 * at the specified address.  Clear the NOINTERNFIXUPS flag
                 * and try again.
                 */
                pmte->mte_flags1 &= ~NOINTERNFIXUPS;
                // The for-loop will increment this so we re-try the same object
                lobjnum--;
#ifdef  LDRSTRICT
                if ( ldrDebugFlags & LDR_PRINT_BROKEN_BASING )
                   {
                   // Must copy like this because the compiler
                   // blasts ES and printf depends on ES being FLAT
                   ((DWORD *)modname)[0] = ((DWORD *)pmte->mte_modname)[0];
                   ((DWORD *)modname)[1] = ((DWORD *)pmte->mte_modname)[1];
                   modname[8] = 0;                           //Null terminate
                   printf("Cannot load %s at requested base address\n",SSToDS(modname));
                   }
#endif

              }
              else // not a NOINTERNFIXUPS problem
              {
                /* REAL PROBLEM ENCOUNTERED!!!
                 * We must cleanup any objects that were allocated
                 * before the error if the module we are loading
                 * was a library module.  The reason is that the mte for
                 * this module will not have a run time attachment to this
                 * process.  The case we are covering here is if a module
                 * that references another, is not loaded that module's
                 * module pointer table will not get the pointer to the
                 * mte for this one.  Therefore when garbage collection
                 * is run this module will be freed and the allocated
                 * objects will not get freed.
                 */
                for ( lobjnum2 = 1; lobjnum2 <  lobjnum; lobjnum2++ )
                {
                    // Find the pote
                    if ( plv->lv_type == EXT_LIBRARY )
                    {
                        pote = ((ldrote_t *)psmte->smte_objtab)
                                            + ( psmte->smte_objcnt - lobjnum2 );
                    }
                    else
                    {
                        pote = ((ldrote_t *)psmte->smte_objtab)
                                                             + ( lobjnum2 - 1 );
                    }

                    // Free any cached instance data objects
                    if (    (pote->ote_handle != HOBNULL)
                         && !(pote->ote_flags & OBJSHARED)
                         && !(pote->ote_flags & OBJEXEC)
                         && (pote->ote_flags & OBJWRITE) )
                    {
                        if ( !(VMGetLaddr( pote->ote_handle,
                                           SSToDS(&laddr) ) ))
                            VMFreeMem( laddr,
                                       (VMHOB) ( (pPTDACur->ptda_child != HOBNULL) ?
                                                       pPTDACur->ptda_child :
                                                       pPTDACur->ptda_handle ),
                                        0 );
                    }

                    // free any other type of object if allocated
                    else if (pote->ote_flags & OBJALLOC)
                    {
                        VMFreeMem( pote->ote_base,
                                   (VMHOB) ( (pPTDACur->ptda_child != HOBNULL) ?
                                                         pPTDACur->ptda_child :
                                                         pPTDACur->ptda_handle ),
                                    VMFM_NOLDR | VMFM_GC );

                    }
                    // clear allocation flag
                    pote->ote_flags &= ~OBJALLOC;
                }

                // finished freeing the other objects, so exit
                load_error( rc, pmte );
                return( rc );

              } // end of if not NOINTERNFIXUPS

            } // end of bad ret code from ldrallocobject

        } // end of if object is PRELOAD || not a rsrc

    } // end of for each object

    /*
     * Set the flag in the MTE to indicate we allocate address for
     * instance data.
     */
    pmte->mte_flags1 |= MTEADDRALLOCED;

    /*
    * allocate or attach to all gates for this module
    */
    if ( rc = ldrEachObjEntry( 0,
                               pmte,
                               ldrGetCallGate,
                               0 ) )
    {
        load_error(rc, pmte);
    }

    return(rc);

} // end of ldrAllocObjects

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




/***LP  ldrSetVMflags - Convert object flags to VM flags
 *
 *      Check the flags for this object, convert to VM flags for call
 *      to VMAlloc or VMAttach.
 *
 *      ENTRY   pmte - pointer to module table entry
 *              fl - flags of object to check
 *              pfl - pointer to allocation flags
 *              pflsel - pointer to selector alloction flags
 *
 *      EXIT    pfl and pflsel - Flags set to inital state
 *
 *
 *   This procedure performs the following steps:
 *
 *      - set object type flags
 *      - set initial allocation flags
 */

void ldrSetVMflags( ldrmte_t  *pmte,    /* pointer to module table entry */
                    ulong_t   fl,       /* flags of object to check */
                    ulong_t   *pfl,     /* pointer to allocation flags to ret */
                    ushort_t  *pfssel ) /* ptr to selector allocation flag */
{
    ulong_t iasobjmem;    // index to objmem
    int     i;

    // Set the initial state for all objects
    //
    // BUGBUG:
    //      For OS/2 2.0 force all objects allocated to have selectors
    //
    // Set VMAC_RETAIN bit for all 32-bit module. This means retain
    // the address space after the object has been freed.
    *pfl |= VMAC_SELALLOC | VMAC_ALIGNPAGE | PG_U |
                                     ( (fl & OBJRSRC) ? 0 : VMAC_RETAIN );

    // Check for non-writable objects, make sure it is allocated as
    // shared
    if ( !(fl & OBJWRITE) )
        *pfl |= VMAC_SHARED;

    // Using the object table entry flags scan array with allocation flags
    // for VM interface. The structure of the array can be found
    // in LDRINIT.C
    for ( i = 0; objflags[i].flobj != 0; i++ )
    {
        if (fl & objflags[i].flobj)
            *pfl |= objflags[i].flVM;
    }

    // For IOPL objects set selector to RING 2 else make them RING 3
    if (fl & OBJIOPL)
    {
        // No ring 3 access if not conforming
        if ( !(fl & OBJCONFORM) )
            *pfl &= ~PG_U;

        *pfssel |= SELAL_SELMAP | (SEL_RPL2 << SELAL_PRIVSHIFT);

        // Set if module has IOPL segment
        fTKUsesR2 = TRUE;
    }
    else
    {
        *pfssel |= SELAL_SELMAP | (SEL_RPL3 << SELAL_PRIVSHIFT);
    }

    for ( i = 0; selflags[i].flobj != 0; i++ )
    {
        if (fl & selflags[i].flobj)
            *pfssel |= selflags[i].fsSEL;
    }

    /*
     * compute the index into a structure for the initial allocation type,
     * the structure contains the flags for the call to VMAlloc to make
     * the allocation.
     * The index is made up of the following fields:
     *
     *     bit      meaning if bit  meaning if bit
     *              not turned on   turned on
     *     ---      -------------   --------------
     *      0       non-sparse      sparse
     *      1       readonly        read/write
     *      2       no preload      force preload
     *
     *      index   force    readonly        sparse
     *      number  preload  read/write  non-sparse
     *      ------  -------  ----------  ----------
     *        0       no     readonly    non-sparse
     *        1       no     readonly    sparse
     *        2       no     read/write  non-sparse
     *        3       no     read/write  sparse
     *        4       yes    readonly    non-sparse
     *        5       yes    readonly    sparse
     *        6       yes    read/write  non-sparse
     *        7       yes    read/write  sparse
     *
     * For this to work correctly OBJWRITE must be 0x2 and OBJINVALID
     * must be 0x80
     */
    ldrAssert( OBJWRITE == 0x2L && OBJINVALID == 0x80L );

    iasobjmem = (fl & OBJWRITE) | ((fl & OBJINVALID) >> 7);

    if (!(pmte->mte_flags1 & MTE_MEDIAFIXED))
        iasobjmem |= (1<<2);

    *pfl |= objmem[iasobjmem].flobj;

    return;

} // end of ldrSetVMFlags



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


/***LP  ldrProcessObjects - Process each object page map entry if needed
 *
 *      The object for this module have been allocated already, the
 *      modules referenced have been loaded.  Now re-scan the object
 *      table entries looking to see if the object page table entries
 *      need to be scanned.  While scanning the page table entries for
 *      each object a state change may be needed for an individual page
 *
 *      ENTRY   pmte                    - point to the MTE
 *
 *      EXIT    none                    - Page table entries scanned for each
 *                                        object
 *
 *   This procedure performs the following steps:
 *
 *      - For each object in this module
 *      -   if object shared and not first load of this module skip this object
 *      -   if this object is readonly and zero fill error out
 *      -   For each page in this object
 *      -     do state change on page if required
 *      -     load preload pages if required
 *      -   load only preload that were not memory resident
 *      - Free memory used for preload pages
 */

int ldrProcessObjects( ldrmte_t *pmte )
{
    register ldrsmte_t *psmte;          // pointer to swappable mte
    ptda_t             *pptda;          // Current or ExecChild ptda
    register ldrote_t  *pote;           // pointer to object table entry
    ldropm_t           *popm;           // pointer to object page map
    ulong_t             lobjnum;        // object number index
    ulong_t             pBase;          // real base addr of object
    ldrpip_t            pip;            // Working Page Info Packet
    int                 fCacheable;     // Object cacheable flag
    int                 fProcess;       // Object is to be process on this pass
    int                 rc;             // save return codes
    VMAC                ac;             // VMAllocMem buffer
    int                 i;

    // Initialisation
    psmte = pmte->mte_swapmte;
    rc = (int)( ac.ac_va = pip.pip_pbuf = 0L );

    // Set context - should get us the child's ptda, which is what we want
    pptda = TKContext();

    // for each object ...
    for ( lobjnum = 0, pote = (ldrote_t *) psmte->smte_objtab;
          lobjnum < psmte->smte_objcnt;
          lobjnum++, pote++ )
    {

        // Only process the object if ...
        // Note ignoring preload requests.
        // 4meg perf, also preload shared objects for system DLL's (except rsrc)
        fProcess = (   !(pmte->mte_flags1 & MTE_MEDIAFIXED)  // removable
                    || (pmte->mte_flags1 & VDDMOD)           // VDD
                    || (pmte->mte_flags1 & DEVDRVMOD)        // DD or IFS
                    || ((pmte->mte_flags2 & MTESYSTEMDLL)    // System DLL
                        && (pote->ote_flags & OBJSHARED)     //   (shared,
                        && !(pote->ote_flags & OBJRSRC)));   //    not rsrc)

        // Do not load if it is already loaded unless we need instance
        // data ...
        if (    ( fProcess )
             && ( pmte->mte_flags1 & USED ) )
            fProcess = !( pote->ote_flags & (OBJEXEC | OBJSHARED) );

        // If no pages in the page map table, only load if it is a VDD
        if (    ( fProcess )
             && ( !(pmte->mte_flags1 & VDDMOD) )
             && ( !(pmte->mte_flags1 & DEVDRVMOD) )
             && ( !(pote->ote_mapsize) )  )
            fProcess = 0;

        // Process this object dependant on decisions above
        if (fProcess)
        {

            // Check cache not in a confused state before we start
            ldrAssert( pote->ote_handle != LDRMKCACHEOWNER );

            // Decide whether to cache or not it is necessary to cache
            // the object.
            fCacheable =
                   !( pmte->mte_flags1 & MTE_MEDIAFIXED )         // removeable
                &&  ( pmte->mte_flags1 & LIBRARYMOD )             // is a DLL
                && !( pmte->mte_flags1 & DEVDRVMOD )              // not a PDD
                && !( pote->ote_flags & ( OBJSHARED | OBJEXEC ) ) // not shared
                &&  (     (pote->ote_handle!=HOBNULL)      // already cached
                      || !(pmte->mte_flags1 & USED)        // not loaded already
                    );

            // If cache needs to be made
            if ( fCacheable && ( pote->ote_handle == HOBNULL ) )
            {
                // Flag to say cache in process of being made
                pote->ote_handle = LDRMKCACHEOWNER;

                // Allocate a cache.
                // Use ote_mapsize instead of ote_size as the zero fill pages
                // implicit on the end of an object should not be cached.
                if ( VMAllocMem( pote->ote_mapsize << PAGESHIFT,
                                 pote->ote_mapsize << PAGESHIFT,
                                 VMS_COMMIT | VMAC_ARENASYS | PG_R | PG_W | PG_SWAPPABLE,
                                 HOBNULL,
                                 (VMHOB) LDRCACHEOWNER,
                                 HOBNULL,
                                 0,
                                 0,
                                 SSToDS(&ac) ) )
                {
                    // If allocation failed, reset handle and set flag off
                    pote->ote_handle = HOBNULL;
                    fCacheable = FALSE;
                    ac.ac_va = 0L;
                    ldrStop(1, pmte);
                }
            }

            // Save object's address
            if (pmte->mte_flags1 & VDDMOD)
                pBase = pote->ote_vddaddr;
            else
                pBase = pote->ote_base;

            // The start of the object page table for this object
            popm = ((ldropm_t *)psmte->smte_objmap)
                                              + ( pote->ote_pagemap - 1 );

            // If the object is a PDD or VDD, we need to zero fill any pages
            // not defined in the page map table as we are not going
            // to fault them in. If the object is not a PDD or VDD, we will
            // only fill such zero fill pages on a page fault.
            if (   ( pmte->mte_flags1 & (DEVDRVMOD | VDDMOD) )
                && ( pote->ote_size > (pote->ote_mapsize<<PAGESHIFT) ) )
            {
                ldrResetDD( pBase + (pote->ote_mapsize<<PAGESHIFT),
                            pote->ote_size - (pote->ote_mapsize<<PAGESHIFT) );
            }

            // Note that ldrGetPages will only read in 8 pages due to
            // limitations on heap storage (MAX_DEMAND_READ). See
            // PGMAP.C and PGAllocBuf.

            // Start at the first page
            pip.pip_spbf = 0;

            // Pages in the object
            pip.pip_maxp = pote->ote_mapsize;

            // Number of pages for first (possibly only) blocked read
            if ( ( pip.pip_cpbf = pote->ote_mapsize ) > MAX_DEMAND_READ )
                pip.pip_cpbf = MAX_DEMAND_READ;

            // Loop to read blocks of 8 pages until we have had enough
            while ( pip.pip_cpbf )
            {

                // Read from the file and unpack iterated pages
                if ( rc = ldrGetPages( pmte,
                                       pmte->mte_sfn,
                                       SSToDS(&pip),
                                       popm,
                                       pote ) )
                {
                    ldrStop( 2, pmte );

#ifdef LDRSTRICT
                    if (    rc!=0
                         && rc!=ERROR_NOT_ENOUGH_MEMORY
                         && rc!=ERROR_INTERRUPT
                         && rc!=ERROR_INVALID_ORDINAL
                         && rc!=ERROR_PROC_NOT_FOUND
                         && rc!=ERROR_BAD_EXE_FORMAT
                         && rc!=ERROR_INVALID_SEGMENT_NUMBER
                         && rc!=ERROR_INVALID_CALLGATE
                         && rc!=ERROR_LDR_FWD )
                        panic("LDRProcessObjects: bad ldrGetPages");
#endif

                    goto process_error;
                }

                // Apply fixups to each page in the object
                for ( i = 0;
                      i < pip.pip_cpbf;
                      i++ )
                {
                    if ( rc = ldrEnum32bitRelRecs( pmte,
                                                   lobjnum,

                                                   // logical page number for page
                                                   i + pip.pip_spbf + pote->ote_pagemap - 1,

                                                   pip.pip_pbuf + (i<<PAGESHIFT),
                                                   pBase + ((i+pip.pip_spbf)<<PAGESHIFT),
                                                   pPTDACur ) )
                    {
                        ldrStop(3, pmte);

#ifdef LDRSTRICT
                        if (    rc!=0
                             && rc!=ERROR_NOT_ENOUGH_MEMORY
                             && rc!=ERROR_INTERRUPT
                             && rc!=ERROR_INVALID_ORDINAL
                             && rc!=ERROR_PROC_NOT_FOUND
                             && rc!=ERROR_BAD_EXE_FORMAT
                             && rc!=ERROR_INVALID_SEGMENT_NUMBER
                             && rc!=ERROR_INVALID_CALLGATE
                             && rc!=ERROR_LDR_FWD )
                            panic("LDRProcessObjects: bad ldrEnum32bitRelRecs");
#endif

                        goto process_error;
                    }
                }

                // Copy the pages into the cache
                if ( fCacheable && ac.ac_va )
                {
                    memcpy( (char *)ac.ac_va + (pip.pip_spbf<<PAGESHIFT),
                            (char *)pip.pip_pbuf,
                            pip.pip_cpbf<<PAGESHIFT );
                }

                // Handle VDDs as a special case - storage already there
                // and is not writeable for code.
                if ( pmte->mte_flags1 & (VDDMOD|DEVDRVMOD) )
                {
                    // It is a VDD ? - copy the pages. Note that VDD
                    // objects do not necessarily start on page boundarys
                    // and do not necessarily occupy whole pages.

                    ldrCopyVDD( // Target address to copy to.
                                pBase + (pip.pip_spbf<<PAGESHIFT),

                                // Where to copy from.
                                pip.pip_pbuf,

                                // How much to copy.
                                // More than 8 pages ? If so copy the lot.
                                ( pote->ote_mapsize - pip.pip_spbf ) > 8
                                    ? 8<<PAGESHIFT
                                    :
                                      // Less than 8 pages - where do they end as they
                                      // may not all have been read from the file.
                                      ( pote->ote_size > (pote->ote_mapsize<<PAGESHIFT)
                                            ? pip.pip_cpbf<<PAGESHIFT
                                            : pote->ote_size - (pip.pip_spbf<<PAGESHIFT)
                                      )
                              );
                }
                else
                {
                    // Is not a VDD - "move" the pages
                    rc = PGPreload( pip.pip_pbuf >> PAGESHIFT,
                                    (pote->ote_base >> PAGESHIFT) + pip.pip_spbf,
                                    pptda,
                                    pip.pip_cpbf,
                                    -1,
                                    !(pmte->mte_flags1 & MTE_MEDIAFIXED) );
                }

                // and "free" the pages
                PGFreeBuf( pip.pip_pbuf );
                pip.pip_pbuf = 0L;

                if (rc)
                {
                    ldrStop(4, pmte);
                    goto process_error;
                }

                // Set up for next iteration if required
                // 4meg perf, also system DLL's
                if (     ( pmte->mte_flags1 & VDDMOD )
                     || !( pmte->mte_flags1 & MTE_MEDIAFIXED )
                     || (pmte->mte_flags2 & MTESYSTEMDLL)   // System DLL
                     ||  ( pmte->mte_flags1 & DEVDRVMOD ) )        //76354
                {
                    // Is a VDD, or forcing preload for removable media,
                    // any more pages to read ?
                    pip.pip_spbf += pip.pip_cpbf;
                    if ( ( pip.pip_cpbf = pote->ote_mapsize - pip.pip_spbf
                                                       ) > MAX_DEMAND_READ )
                        pip.pip_cpbf = MAX_DEMAND_READ;
                }
                else
                {
                    // not a VDD - just want to exit after first 8 pages
                    pip.pip_cpbf = 0L;
                }

            } // end of for each block of 8 pages in object

            // Now complete the cache, if we were creating a cache
            if ( fCacheable && ( pote->ote_handle == LDRMKCACHEOWNER ) )
            {
                // Set the handle so we can find the cache later
                pote->ote_handle = ac.ac_hob;

                // and reset the pointer for next time around
                ac.ac_va = 0L;
            }

        } // end of process this object on this pass or not

    }  // end of for each object

process_error:

    // Free up the cache if we had created one but not used it
    if (ac.ac_va)
        VMFreeMem( ac.ac_va,
                   HOBNULL,
                   0 );

    // Free any previous PGAllocBuf pages not yet freed
    if (pip.pip_pbuf)
        PGFreeBuf( pip.pip_pbuf );

    if (rc) {
        load_error( rc, pmte );
    } else {
         // PERF - defect 98488
         // if the free fixup flag is on, free the fixups and clear the bit
         if (pmte->mte_flags2 & MTEFREEFIXUPS)  {
              ldrFreeMTEFixups(pmte,1);  // perf enh
              pmte->mte_flags2 &= ~MTEFREEFIXUPS;
         }
    }
    return(rc);

} // end of ldrProcessObjects



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


/***LP  ldrGetPages - read a range of pages to force preload
 *
 *      When force preload is required for all pages of an executable
 *      ldrGetPages is called to read the pages into memory.
 *      ldrGetPages will unpack iterated data and handle zero fill
 *      pages, but does not apply fixups.
 *
 *      ENTRY   pmte                    - pointer to a module table entry
 *              sfn                     - open file handle of the module
 *              ppip                    - pointer to page info packet
 *              popm                    - pointer into the object page table
 *              pote                    - pointer into the object tabel entry
 *
 *      EXIT    none
 *
 *      EFFECTS ppip->pip_cpbf          - set to # of pages read
 *              ppip->pip_spbf          - set to total pages read from exe
 *              ppip->pip_pbuf          - set to linear addr of buffer read
 *                                        PGAllocBuf allocated
 */

int ldrGetPages( pmte, sfn, ppip, popm, pote )
ldrmte_t          * pmte;   // Pointer to a module table entry.
ushort_t            sfn;    // Handle of open file to read from
register ldrpip_t * ppip;   // Pointer to page info packet.
ldropm_t          * popm;   // Pointer to first entry in object page map
                            // for this object.
ldrote_t          * pote;   // Pointer to the object table entry for
                            // this object.
{
    ulong_t taddr;
    int i, j, First, Last;
    ulong_t StartPos, Size;
    char * pSrc;
    char * pTrg;
    int rc = 0;

    // Free any previous PGAllocBuf pages, because pages are now mapped
    // to another physcial location.
    if ( ppip->pip_pbuf != NULL )
    {
        PGFreeBuf( ppip->pip_pbuf );
        ppip->pip_pbuf = NULL;
    }

#ifdef LDRSTRICT
    // Check that we are not being told to read pages not in the file
    if ( ppip->pip_spbf + ppip->pip_cpbf > ppip->pip_maxp + 1 )
        panic("LDRGetPages: pages not in file");
#endif

    // Allocate the storage to read into, exit if error.
    if (rc = PGAllocBuf( ppip->pip_cpbf,
                           ((pmte->mte_flags1 & MTE_MEDIACONTIG) ? PG_CONTIG : 0)
                         | ((pmte->mte_flags1 & MTE_MEDIA16M) ? PG_16M : 0)
                         | PG_DISCARDABLE,
                         -1,                  // need physical memory
                         &ppip->pip_pbuf,
                         SSToDS(&taddr),
                         0 ) )
    {

#ifdef LDRSTRICT
        // go thru error handling instead of panic on out of memory
        if (    rc!=ERROR_NOT_ENOUGH_MEMORY
             && rc!=ERROR_INTERRUPT )
            panic("LDRGetPages: bad PGAllocBuf");
#endif

        ppip->pip_pbuf = 0L;
        return (rc);
    }

    // Pages are in the cache ? - use the cache instead of reading from disk
    if (    !( pmte->mte_flags1 & MTE_MEDIAFIXED )
         && ( pmte->mte_flags1 & LIBRARYMOD )
         && ( pote->ote_handle != HOBNULL )
         && ( pote->ote_handle != LDRMKCACHEOWNER )
         && !( pote->ote_flags & (OBJSHARED | OBJEXEC) )
         && !( pmte->mte_flags1 & VDDMOD) )      /* added VDD check -   82707*/
    {
        // Find the address of the start of the cache
        rc = VMGetLaddr( pote->ote_handle,
                         SSToDS(&taddr) );
#ifdef LDRSTRICT
        if (    rc!=0
             && rc!=ERROR_INTERRUPT )
            panic("LDRGetPages: bad VMGetLaddr");
#endif

        // copy the data out of the cache
        memcpy( (char *)ppip->pip_pbuf,
                (char *)(taddr + ( ppip->pip_spbf << PAGESHIFT ) ),
                ppip->pip_cpbf << PAGESHIFT );

        return( rc );
    }

    // Find a range of pages that we can read in one go ... the page
    // data offsets must be contiguous and the pages must actually
    // exist in the file (ie iterdata or valid).
    Last = ppip->pip_spbf - 1;
    while ( Last < (ppip->pip_spbf + ppip->pip_cpbf - 1) )
    {
        // Starting conditions for next scan
        i = First = Last + 1;

        // Scan up through the allocated pages to identify a range
        while ( 1 )
        {
            // check the flags of the current page
            if (   ( popm[i].o32_pageflags != VALID )
                && ( popm[i].o32_pageflags != ITERDATA )
                && ( popm[i].o32_pageflags != ITERDATA2 ) )
            {
                Last = i-1;
                if (Last<First)
                    Last = First;
                break;
            }

            // check that we have some more pages to go
            if ( i >= (ppip->pip_spbf + ppip->pip_cpbf - 1) )
            {
                Last = i;
                break;
            }

            // check that the next page is in sequence
            if ( popm[i+1].o32_pagedataoffset
                     != ( popm[i].o32_pagedataoffset
                            + (  ( popm[i].o32_pagesize
                                   + (1<<pmte->mte_swapmte->smte_pageshift) -1 )
                                     >> pmte->mte_swapmte->smte_pageshift
                              )
                        )
               )
            {
                Last = i;
                break;
            }

            // good range so far ... go round for the next page
            i++;

        }  // end of while 1

        // Did we hit a single zero fill or invalid and not get
        // a range ??
        if (   ( popm[First].o32_pageflags != VALID )
            && ( popm[First].o32_pageflags != ITERDATA )
            && ( popm[First].o32_pageflags != ITERDATA2 ) )
        {
            // expand the single zero fill page
            memset( ((char *)ppip->pip_pbuf) + ( (First - ppip->pip_spbf)
                                                                 << PAGESHIFT ),
                    '\0',
                    (ulong_t)PAGESIZE );

        }
        else  // we have a range to read in
        {

            // Work out number of bytes to read in
            Size = (  (   popm[Last].o32_pagedataoffset
                                               - popm[First].o32_pagedataoffset
                      ) << pmte->mte_swapmte->smte_pageshift
                   ) + popm[Last].o32_pagesize;

            // Work out where to start the read
            StartPos = pmte->mte_swapmte->smte_datapage
                         + ( popm[First].o32_pagedataoffset <<
                                            pmte->mte_swapmte->smte_pageshift );

            // Read the pages in, exit if error.
            if ( rc = ldrRead( sfn,
                               StartPos,
                               ((char *)ppip->pip_pbuf) +
                                      ( (First - ppip->pip_spbf) << PAGESHIFT ),
                               taddr +
                                      ( (First - ppip->pip_spbf) << PAGESHIFT ),
                               Size,
                               pmte ) )
                 return(rc);

            // Due to page alignment and truncated pages and iterated data, we
            // now have to shuffle the data we read in into the correct place
            // in memory to match the pages. Go thru the pages in reverse order
            // or we will trample all over ourselves.
            for ( i = Last ; i > First ; i-- )
            {
                // Work out where the source and target for each page are
                pTrg = ((char *)ppip->pip_pbuf) + ( (First - ppip->pip_spbf)
                                                                 << PAGESHIFT );
                pSrc = pTrg + ( ( popm[i].o32_pagedataoffset
                                    - popm[First].o32_pagedataoffset )
                                         << pmte->mte_swapmte->smte_pageshift );
                pTrg += ( i - First ) << PAGESHIFT;

                // No need to move if the data is in the right place
                if ( pSrc != pTrg )
                {
                   // Move the data, working backwards so that we don't trample
                   // all over ourselves. Do NOT use memcpy as source and
                   // destination conflict.
                   for ( j = popm[i].o32_pagesize-1; j>=0; j-- )
                       *(pTrg+j) = *(pSrc+j);

                } // end of if pSrc!=pTrg

            } // end of for each page in reverse order

            // zero fill and unpack each page if necessary
            for ( i = First; ( i <= Last ) && (!rc) ; i++ )
            {
                // zero fill
                if ( popm[i].o32_pagesize < PAGESIZE )
                {
                    memset( ((char *)ppip->pip_pbuf) +
                                ( ((i-ppip->pip_spbf)<<PAGESHIFT) +
                                                        popm[i].o32_pagesize ),
                            '\0',
                            (ulong_t)PAGESIZE - popm[i].o32_pagesize );
                }

                // Then unpack. Note that UnpackPage can handle a file read
                // if we want it to - we instead pass 0 as file offset/sfn
                // as we wanted to block the reads ourself.
                if ( popm[i].o32_pageflags == ITERDATA )
                {
                    rc = ldr32UnpackPage( 0,
                                          ((char *)ppip->pip_pbuf)
                                             + ((i-ppip->pip_spbf)<< PAGESHIFT),
                                          0,
                                          0,
                                          (unsigned long)popm[i].o32_pagesize );
                }

                else if ( popm[i].o32_pageflags == ITERDATA2 )
                {
                    rc = ldr32UnpackPage2( 0,
                                          ((char *)ppip->pip_pbuf)
                                             + ((i-ppip->pip_spbf)<< PAGESHIFT),
                                          0,
                                          0,
                                          (unsigned long)popm[i].o32_pagesize );
                }

            } // end of zero fill and unpack each page

        } // end of if range to read in

    } // end of while more in the block

    return(rc);

} // end of ldrGetPages


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

/***EP  LDRGetSegment - Preload a segment for the module.
 *
 *      LDRGetSegment will be called by the SEL memory code for LDTPreloading
 *      of segments and also for the preloading of GDT selectors.  The MTE
 *      handle of the module will be passed, plus the linear address of the
 *      segment to preload.
 *
 *      ENTRY   hobmte                  - mte handle
 *              laddr                   - linear address of segment to load
 *              sel                     - selector of segment to load
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *      REQUIRED                        - must be in the context of process
 *                                        who calls this.
 *
 *   This procedure performs the following steps:
 *
 *      - Map MTE handle to a linear addr and validate MTE signature.
 *      - find ste of segment to load.
 *      - allocate buffer to load into
 *      - load segment
 *      - move pages to final linear addresses
 */

int LDRGetSegment( VMHOB   hobmte,  /* handle to mte for seg to load */
                   ulong_t laddr,   /* linear address to load at */
                   SEL     sel )    /* selector of segment to load */
{
    ldrlf_t            lf;             /* buffer for ldrsegment */
    ldrlv_t            lv;             /* allocate local variables */
    ldrmte_t          *pmte;           /* pointer to module table */
    ldrsmte_t         *psmte;          /* pointer to swappable mte */
    register ldrste_t *pste;           /* pointer to segment table */
    ulong_t            segnum;
    ulong_t            lpackaddr;
    int                rc;

    /*
     * Init local variables
     */
    memset( (char *)SSToDS(&lv), 0, (ulong_t)(sizeof(ldrlv_t)) );

    lf.lf_pallocbuf = lf.lf_faultaddr = lf.lf_objaddr = laddr;

    // find mte etc
    lv.lv_hobmte = lf.lf_hMTE = hobmte;
    pmte = lv.lv_pmte = ldrpMTEFromHandle(hobmte);
    psmte = pmte->mte_swapmte;
    lv.lv_sfn = pmte->mte_sfn;

    /*
     * For segment packing the faulting address needs to be computed
     * using the base load address + the size of each segment that is
     * packed
     */
    lpackaddr = psmte->smte_ssegpack;

    /*
     * Find segment to load, search ste's looking for linear
     * address in segments.
     */
    pste = ((ldrste_t *) psmte->smte_objtab);
    for ( segnum = 1; segnum <= psmte->smte_objcnt; segnum++, pste++ )
    {

        if (pste->ste_selector == sel)
        {
            if (pste->ste_flags & STE_PACKED)
            {
                lf.lf_pallocbuf =
                lf.lf_faultaddr =
                lf.lf_objaddr   = lpackaddr;
            }
            lf.lf_bno = (segnum << 4);

            if ( rc = ldrSegment( pste, SSToDS(&lf), SSToDS(&lv) ) )
            {
                ldrWriteErrTxt( rc, pmte );
            }
            return (rc);
        }

        if (pste->ste_flags & STE_PACKED)
            lpackaddr += (ulong_t) pste->ste_minsiz;

    }

#ifdef LDRSTRICT
    panic("LDRGetSegment: linear address invalid");
#endif

    return( ERROR_INVALID_SEGMENT_NUMBER );

} // end of ldrGetSegment


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


/***LP  ldrAllocObject - allocate a object for requested mte.
 *
 *      For a object of this module call the memory manager to get a
 *      linear address reserved and request the overcommitment.
 *
 *      ENTRY   pmte           - pointer to mte
 *              pote           - pointer to a object table entry
 *              type           - type of object we are loading
 *              pptda          - pointer to a ptda
 *              lobjnum        - object number
 *
 *      EXIT    int            return code (NO_ERROR if successful)
 *
 *      - reserve or attach to memory for this object
 *      - allocate or attach to call gates
 */

int ldrAllocObject( register ldrmte_t *pmte,    // pointer to the module table entry
                    register ldrote_t *pote,    // pointer to the object table entry
                    uchar_t            type,    // type of object we are allocating
                    ptda_t            *pptda,   // Current or ExecChild ptda
                    ulong_t            lobjnum) // object number - 1 based
{
    VMAC        sac;            /* return info from VM or Sel */
    ulong_t     flvmalloc;      /* flags used for VMAlloc call */
    ulong_t     vddfixup;       /* place to get return address */
    ulong_t     objtype;
    ulong_t     cbCommit;       /* Bytes to commit on VMAllocMem */
    int         rc;
    ulong_t     SumBN;          // Calculate logical block number (bno)
    ulong_t     i;
    ushort_t    fsSELalloc = 0; /* SEL flags needed for VMAlloc call */
    ushort_t    bAllocObj;      /* B721564 - object allocated */

    objtype = (pote->ote_flags & OBJTYPEMASK);

    // alloc resources out of the share arena
    if (pote->ote_flags & OBJRSRC)
    {
        flvmalloc = VMAC_ARENASHR;
    }

    else // not a resource
    {
        switch (type)
        {
            case EXT_PROGRAM:       /* user program */
                flvmalloc = VMAC_ARENAPRV;
                break;

            case EXT_LIBRARY:       /* DLL */
                flvmalloc = VMAC_ARENASHR;
                break;

            case EXT_DEVICE:        // device driver
            case EXT_FSD:           // installable file system

                        if ( ( rc = SELAllocKHB( (VMHKH)( (objtype == OBJPERM)
                                                          ? ( (pote->ote_flags & OBJWRITE)
                                                              ? VM_HKSH_PUBRW
                                                              : VM_HKSH_PUBRO
                                                            )
                                                          : ( (pote->ote_flags & OBJWRITE)
                                                              ? VM_HKRH_PUBRW
                                                              : VM_HKRH_PUBRO
                                                            ) ),

                                                 pote->ote_size,

                                                 PG_U | ( (objtype == OBJCONTIG)
                                                          ? PG_CONTIG
                                                          : NULL ),

                                                 ( (pote->ote_flags & OBJEXEC)
                                                   ? ( (pote->ote_flags & OBJREAD)
                                                       ? D_RX
                                                       : D_X
                                                     ) | D_CODE
                                                       | D_DPL0
                                                       | D_CONFORM
                                                   : ( (pote->ote_flags & OBJWRITE)
                                                       ? D_W
                                                       : D_R
                                                     ) | D_DATA | D_DPL3 ),

// 147968                                          (VMHOB)( (type == EXT_DEVICE)
// 147868                                                   ? LDRDDOwner
// 147968                                                   : LDRFSDOwner ),
                                                   (VMHOB) (pmte->mte_handle),

                                                 SSToDS(&sac) ) ) != NO_ERROR )
                        {
                            ldrStop( 0, pmte );
                            return(rc);
                        }

                        pote->ote_base = sac.ac_va;
                        pote->ote_selector = sac.ac_sel;
                        return( NO_ERROR );

            case EXT_VDD:

                // Check for a VDD module, if so call VDMM to assign address
                // for all objects in this module.
                if ( (pote->ote_vddaddr =
                                (ulong_t) VDMMAssignAddress( pote->ote_size,
                                                             pote->ote_flags,
                                                             SSToDS(&vddfixup)
                                                            ) ) == 0 )
                {
                    ldrStop( 1, pmte );
                    return( ERROR_NOT_ENOUGH_MEMORY );
                }
                else
                {
                    pote->ote_base = vddfixup;
                    pote->ote_flags |= OBJALLOC;
                }
                return( NO_ERROR );

#ifdef LDRSTRICT
            default:
                panic("LDRSTE: unknown module type");
#endif
        }
    }

    // Setup for the call to VM to allocate or attach to memory for
    // this object convert objects flags to vm allocation flags
    ldrSetVMflags( pmte,
                   pote->ote_flags,
                   SSToDS(&flvmalloc),
                   SSToDS(&fsSELalloc) );

//  if (    (type == EXT_LIBRARY)
//       && ( (pote->ote_flags & (OBJWRITE | OBJSHARED)) == (OBJWRITE | OBJSHARED) )
//       && (pmte->mte_flags1 & MTEMODPROT) )
//  {
//      flvmalloc |= VMAC_PROT;
//  }

    // Check if object shared or code, if it is it may be allocated
    // already we only need to attach to it, else allocate
    if (    (pote->ote_flags & (OBJEXEC | OBJSHARED))
         && (pote->ote_handle != HOBNULL) )
    {
        if ( rc = VMAttach( pote->ote_handle,
                            pptda->ptda_handle,
                            0 ) )
        {
            ldrStop( 2, pmte );
            return(rc);
        }

        bAllocObj = FALSE;    // Object not allocated by this call
    }
    else   // allocate the object
    {

#if 0
        if (    (pote->ote_flags & OBJIOPL)
             && !(pmte->mte_flags1 & MTEIOPLALLOWED) )
        {
            ldrStop( 3, pmte );
            return( ERROR_IOPL_NOT_ENABLED );
        }
#endif
        sac.ac_va = pote->ote_base;

        // Check to see if this is the first alloc - if it isn't then
        // we need to allocate specific.  Or if the linker has applied
        // the internal fixups already, we need to try and place the
        // objects at the pre-relocated addresses.  b730695
        // Note that resources are not based and may not have fixups.
        if  (    (    (pmte->mte_flags1 & MTEADDRALLOCED)
                   || (pmte->mte_flags1 & NOINTERNFIXUPS) )
              && !(pote->ote_flags & OBJRSRC)  )
        {
            flvmalloc |= VMAC_LOCSPECIFIC;

            // If we expect this address range to be already alloced, then
            // we need to pass in the VMAC_PRELOAD flag. If we do not pass
            // in this flag, then VM will pass us back error ERROR_BUSY.
            // Can only be alloced alread if it is in the shared arena.
            if (    VMShrArena(sac.ac_va)
                 && ( pmte->mte_flags1 & MTEPROCESSED ) )
            {
                flvmalloc |= VMAC_RELOAD;
            }

        }

        cbCommit = (flvmalloc & VMS_COMMIT) ? pote->ote_size
                                            : 0;

        // Calculate the block number that will be used by the page manager.
        // The page manager will call ldrGetPage with a block number to
        // identify which page faulted. Allocate block numbers such that :
        //
        // If obj 1 has ote_size of fred, page 3 in object 2 has a block number
        // of ( ( fred + 0xFFF >> PAGESHIFT ) + 3 ).
        //
        // Note that the page manager only uses the low 20 bits !!!!
        //
        // Sum block numbers for the preceding objects.
        for ( i=1, SumBN=1L; i<lobjnum; i++ )
            SumBN += ( (pote-i)->ote_size + 0x00000FFF ) >> PAGESHIFT;

        // Do not discard pages if the media is removable.
        if ( !(pmte->mte_flags1 & MTE_MEDIAFIXED) ||
              /*  perf enh - since the swapper is much faster than the loader
               *             put the system dlls in the swap file
               */
               pmte->mte_flags1 & MTESWAPONLOAD)   // perf-enh
        {
            flvmalloc &= ~(PG_DISCARDABLE | PG_SWAPONWRITE);
            flvmalloc |= PG_SWAPONLOAD;
        }


        if ( rc = VMAllocMem( pote->ote_size,
                              cbCommit,
                              flvmalloc & ~(VMS_COMMIT),    // do not commit ... we will use the
                                                            // PGPreload call when doing the load to
                                                            // actually put the page at the correct
                                                            // address - PGPreload will not let us do
                                                            // this if the storage is allocated committed.
                              (VMHOB) pptda->ptda_handle,
                              (VMHOB) ( (pmte->mte_flags1&FSDMOD      //for FSDs allocated from
                                              && objtype==OBJDYNAMIC) //system space set owner
                                          ? FSIFSOWNER                //to FSIFSOWNER.
                                          : (
                                              (pote->ote_flags &
                                                    (OBJSHARED|OBJEXEC)) //for instance data handle
                                                 ? pmte->mte_handle      //owner is the ptda handle,
                                                 : pptda->ptda_handle    //else it is the mte handle
                                            )
                                      ),
                              (VMHOB) pmte->mte_handle,
                              fsSELalloc,
                              SumBN, // This field becomes the bno (block number)
                                     // passed to ldrGetPage by the page manager
                                     // on a page fault

                              SSToDS(&sac) ) )
        {
            ldrStop( 4, pmte );
            return(rc);
        }

        bAllocObj = TRUE;  // B721564 - Object allocated by this call

        // Only save handle returned by VM if not an instance object
        if (pote->ote_flags & (OBJSHARED | OBJEXEC))
            pote->ote_handle = sac.ac_hob;

        pote->ote_base = sac.ac_va;
        pote->ote_selector = sac.ac_sel;
        pote->ote_flags |= OBJALLOC;

    }

    // Do we need to create a resource record ?
    // If so, place in the record the RSRC32TYPEID constant
    // object number and the mte handle.  Since a object may
    // have more than one resource in it we only need to create
    // one resource record for all these resources.
    if (pote->ote_flags & OBJRSRC)
    {
        if ( rc = ldrRsrcAlloc( RSRC32TYPEID,
                                lobjnum,
                                pmte->mte_handle,
                                pptda ) )
        {
            ldrStop( 5, pmte );
            if (bAllocObj)
            {
                VMFreeMem( sac.ac_va,
                           (VMHOB) pptda->ptda_handle,
                           0 );

                // if (pote->ote_flags & (OBJSHARED | OBJEXEC))
                    pote->ote_handle = 0;
                pote->ote_base = 0;
                pote->ote_selector = 0;
                pote->ote_flags &= ~(OBJALLOC);

            }
            return(rc);
        }

        if (pote->ote_flags & OBJPRELOAD)
        {
            ldrIncRefCount( RSRC32TYPEID,
                            lobjnum,
                            pmte->mte_handle,
                            pmte );
        }
    }

    return(NO_ERROR);

} // end of ldrAllocObject



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


/***EP  LDRGetPage - Demand load a page within a object for the module.
 *
 *      The demand loading of a page results from a not-present
 *      page fault.  This fault is initially handled by the Page
 *      Manager (PM).  The PM validates access to the page by the
 *      current process, obtains the page semaphore, and calls this
 *      routine to load the contents of the page.
 *
 *      ENTRY   plf                     - Pointer to parameter structure
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *      plf->lf_bno is composed of a word for a 16-bit module, the segment
 *      number is shifted to the left by 4.
 *
 *      For a 32-bit module it is just the logical page number to load.
 *
 *
 *   This procedure performs the following steps:
 *
 *      - convert handle to pointer to mte
 *      - check if 16-bit module or 32-bit module
 *      - for 16-bit modules
 *      -   call ldrSegment
 *      - for 32-bit modules
 *        - use magic cookie to load page from object
 */

int LDRGetPage( register ldrlf_t *plf )  /* Pointer to parameter packet */
{
    register ldrmte_t *pmte;          // pointer to module table entry
    ldrsmte_t         *psmte;         // pointer to swappable mte
    register ldrote_t *pote;          // pointer to object table entry
    ldrste_t          *pste;          // pointer to segment table entry
    ldrlv_t            lv;            // allocate local variables on stack
    ldropm_t          *popm;          // pointer to object page map
    unsigned long      lobjnum;       // number of object table entry - 0 based
    unsigned long      SumBN;         // calculate logical block number
    unsigned long      taddr;         // temp Address for cache handling
    int                rc = NO_ERROR;

#ifdef LDRSTRICT
    if ( ldrDebugFlags & LDR_PRINT_ALL_GETPAGE )
        printf( "ldrGP cr2=%lx hMTE=%x bno=%lx\n",
                plf->lf_faultaddr,
                plf->lf_hMTE,
                plf->lf_bno );
#endif

    // Check for call with magic handle, don't process just return.
    if ( plf->lf_hMTE == LDRPRLOBJECT )
        return( rc );

    // Don't throw away the non-res names until we finish here - just in
    // case we need them.
//137076    LDRUseNonResNamSegs();

#ifdef PERFVIEW
    // increment perfview demand load counter
    PvwCnt(mem,DemandLd,0);
#endif

    // Init local variables
    memset( (char *)SSToDS(&lv),
            0,
            (ulong_t)(sizeof(ldrlv_t)) );

    // Point to mte to work with
    pmte = lv.lv_pmte = ldrpMTEFromHandle( lv.lv_hobmte = plf->lf_hMTE );
    psmte = pmte->mte_swapmte;
    lv.lv_sfn = pmte->mte_sfn;

#ifdef LDRSTRICT
    if ( ldrDebugFlags & LDR_PRINT_ALL_GETPAGE )
        printf( "      name = %s\n",
                psmte->smte_path );
#endif

    // Check if it is a 16-bit module
    if ( ldrIsNE(pmte) )
    {
        // Point to the segment table entry
        pste = ((ldrste_t *)psmte->smte_objtab) + ( (plf->lf_bno>>4) - 1 );

        // Process the page fault in the segment code.
        rc = ldrSegment( pste,
                         plf,
                         SSToDS(&lv) );

    } // end of if it is a 16-bit module



    else // Not a 16 bit module, so it must be a 32 bit module
    {

        // Find out which object/page we took the page fault in, and
        // set up pointer to the object table entry for the object.
        // NOTE !!!!! The lf_bno field is passed to us by the Page Manager.
        // The page manager has calculated this field based of the page table
        // index field that we passed it in ldrAllocObject.

#ifdef LDRSTRICT
        // Can never be 0 - starts at 1.
        if ( plf->lf_bno==0 )
            panic("LDRGetPage: bno 0");
#endif

        for ( lobjnum = 0, SumBN = 1L, pote = (ldrote_t *)psmte->smte_objtab;
              lobjnum < psmte->smte_objcnt;
              lobjnum++, pote++ )
        {
            if (    ( SumBN <= plf->lf_bno )
                 && ( plf->lf_bno < (SumBN +
                               ( (pote->ote_size+0x00000FFF) >>PAGESHIFT ) ) ) )
               break;
            SumBN += ( pote->ote_size + 0x00000FFF ) >> PAGESHIFT;
        }

#ifdef LDRSTRICT
        // Did we take a page fault in a non-existant object ?
        if ( lobjnum >= psmte->smte_objcnt )
            panic("LDRGetPage: non-existant object");
#endif

        // Was this in one of our non-existant zero pages, or was it
        // in a genuine page ?
        if ( ( plf->lf_bno - SumBN ) >= pote->ote_mapsize )
        {
            // Non-existant zero page - just zero fill and send back.
            // We can not have fixups for one of these pages.

            memset( (char *)plf->lf_pallocbuf,
                    0,
                    (ulong_t)PAGESIZE );

            // all done - get out
            goto LdrGetPage_exit;

        }

        // Got to here, so must be a real genuine page in the file

        // Set up pointer to the object page table entry for the page
        // we want.
        popm = ((ldropm_t *)psmte->smte_objmap) + ( pote->ote_pagemap - 1
                                                    + plf->lf_bno - SumBN );

        // What sort of page was it ? Process accordingly.
        switch ( popm->o32_pageflags )
        {


            case ZEROED:
                memset( (char *)plf->lf_pallocbuf,
                        0,
                        (ulong_t)PAGESIZE );
                break;


            case VALID:
            case ITERDATA:
            case ITERDATA2:

                // See if it is an instance object - it will be in the
                // cache if we have a cache handle ...
                if (    ( pote->ote_handle != HOBNULL )
                     && ( pote->ote_handle != LDRMKCACHEOWNER )
                     && !( pote->ote_flags & (OBJSHARED|OBJEXEC) )
                     && !( pmte->mte_flags1 & MTE_MEDIAFIXED ) )
                {
                    // Find the address of the start of the cache
                    rc = VMGetLaddr( pote->ote_handle,
                                     SSToDS(&taddr) );

#ifdef LDRSTRICT
                    // It was placed in the cache but now can not be
                    // retrieved ...
                    // if ( rc!=0 && rc!=ERROR_INCACHE && rc!=ERROR_INTERRUPT )
                    //     panic("ldrUseDataCache: bad cache retrieve");
#endif

                    // copy the data out of the cache
                    if (!rc)
                        memcpy( (char *)(plf->lf_pallocbuf),
                                (char *)(taddr + ( (plf->lf_bno - SumBN) << PAGESHIFT ) ),
                                (ulong_t)PAGESIZE );

                    // all done, get out
                    goto LdrGetPage_exit;

                }

                // Not in the cache, so we have to do this the hard way.
                // If it is an iterated page, let UnPack handle it as
                // Unpack will also read from the file.
                // If it is an iterated2 page, let Unpack2 handle it as
                // Unpack2 will also read from the file.
                // Otherwise, just read it from the file ourselves.

                if (popm->o32_pageflags==ITERDATA)
                {
                    // Iterated page - let Unpack read and unpack it.
                    rc = ldr32UnpackPage( pmte,
                                          ((char *)plf->lf_pallocbuf),
                                          psmte->smte_datapage +
                                               (popm->o32_pagedataoffset << psmte->smte_pageshift),
                                          pmte->mte_sfn,
                                          (unsigned long)popm->o32_pagesize );
                }

                else if (popm->o32_pageflags==ITERDATA2)
                {
                    // Iterated page - let Unpack read and unpack it.
                    rc = ldr32UnpackPage2( pmte,
                                          ((char *)plf->lf_pallocbuf),
                                          psmte->smte_datapage +
                                               (popm->o32_pagedataoffset << psmte->smte_pageshift),
                                          pmte->mte_sfn,
                                          (unsigned long)popm->o32_pagesize );
                }

                else // not an iterated page
                {

                    // Read the page from the file
                    rc = ldrRead( pmte->mte_sfn,
                                  psmte->smte_datapage +
                                                (popm->o32_pagedataoffset
                                                      << psmte->smte_pageshift),
                                  (char *)plf->lf_pallocbuf,
                                  plf->lf_sallocbuf,
                                  popm->o32_pagesize,
                                  pmte );

                    // Zero fill the rest of the page if necessary
                    if ( (!rc) && ( popm->o32_pagesize < PAGESIZE ) )
                    {
                        memset( (char *)plf->lf_pallocbuf + popm->o32_pagesize,
                                0,
                                (ulong_t)PAGESIZE - popm->o32_pagesize );
                    }
                }

                break;  // end of case VALID or ITERDATA or ITERDATA2


#ifdef LDRSTRICT
            default:
                panic("LDRGetPage: bad pageflags");
                rc = ERROR_BAD_EXE_FORMAT;
                break;
#endif


        } // end of switch on popm->o32_pageflags

        // Apply fixups to the page
        if (!rc)
            rc = ldrEnum32bitRelRecs( pmte,
                                      lobjnum,

                                      // Logical page number for page - index
                                      // into the fixup table.
                                      plf->lf_bno - SumBN + pote->ote_pagemap - 1,

                                      plf->lf_pallocbuf,
                                      plf->lf_objaddr,
                                      pPTDACur );

    } // end of it is a 32-bit module



LdrGetPage_exit:

    // unlock them as we are all done
//137076    LDRFreeNonResNamSegs();

#ifdef LDRSTRICT

    if ( ldrDebugFlags & LDR_PRINT_BAD_GETPAGE )
        if (rc)
            printf( "ldrGP bad cr2=%lx rc=%lx\n",
                    plf->lf_faultaddr,
                    rc );

    if ( ldrDebugFlags & LDR_INT3_BAD_GETPAGE )
        if (    rc!=0
             && rc!=ERROR_NOT_ENOUGH_MEMORY
             && rc!=ERROR_INCACHE
             && rc!=ERROR_INTERRUPT
             && rc!=ERROR_INVALID_ORDINAL
             && rc!=ERROR_PROC_NOT_FOUND
             && rc!=ERROR_BAD_EXE_FORMAT
             && rc!=ERROR_INVALID_SEGMENT_NUMBER
             && rc!=ERROR_INVALID_CALLGATE
             && rc!=ERROR_INVALID_CALLGATE
             && rc!=ERROR_VC_DISCONNECTED
             && rc!=ERROR_LDR_FWD )
        {
            _asm { int 3 }
        }

#endif

    if (    rc!=0
         && rc!=ERROR_INCACHE
         && rc!=ERROR_INTERRUPT )
    {
        // The page manager will clear up the fault buffer whether or
        // not it is invalidated by ldrWriteErrTxt, so we have no further
        // processing that we need to do.
        ldrWriteErrTxt( rc, pmte );
    }

    // Enhancement for LAN failure handling,
    // defect 71932. Paul.
    // Added other return codes for LAN failure handling. Virginia. #74145
    // Added other return codes(SYS57-59) for LAN failure handling. Ramon.#75589

    if ((rc==ERROR_VC_DISCONNECTED) || (rc==ERROR_BAD_NETPATH)
                                    || (rc==ERROR_ADAP_HDW_ERR)
                                    || (rc==ERROR_UNEXP_NET_ERR)
                                    || (rc==ERROR_BAD_NET_RESP))
    {
       pmte->mte_modname[0] = '-';
    }
    return(rc);

} // end of ldrGetPage



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


/***LP  ldrSegment - allocate buffer for segment load
 *
 *      allocate a buffer to load the segment and call ldrLoadSegment
 *
 *      ENTRY   pste                    - pointer to a segment table entry
 *              plf                     - pointer to ldrlf structure
 *              plv                     - pointer to local variables on stack
 *
 *      EXIT    int                     - return code (NO_ERROR if successful)
 *
 *   This procedure performs the following steps:
 *
 *      - compute number of pages to allocate
 *      - allocate pages with PGAllocBuf
 *      - call ldrLoadSegment to load segment
 *      - copy page requested into buffer passed
 *      - call PM to move other pages to cache
 */

int ldrSegment( register ldrste_t  *pste,   // pointer to segment table entry
                register ldrlf_t   *plf,    // pointer to ldrlf structure
                register ldrlv_t   *plv )   // pointer to local vars on stack
{
    ptda_t            *pptda;
    ldrmte_t          *pmte;         // pointer to module table entry
    ulong_t            ltaddr;       // temp linear address
    ulong_t            cbscratch;    // used to make pages present
    ulong_t            cbseg;        // size of segment in bytes
    ulong_t            npages;       // number of pages to allocate
    ulong_t            cb;
    int                fInstCache;   // instance data cache flag
    VMHOB              hobcache;     // Cache handle
    int                fnopgbuf;     // flag to tell we allocated buffer
    int                fPageable;    // Can segment be paged
    ulong_t            offset;       // page offset of alias within seg
    ulong_t            loadstart;    // starting page to load into
    int                fpgflags;
    int                cloops;
    char              *pBuff = 0;    // Pointer to allocated storage
    struct pagelist_s  pl[17];
    struct pagelist_s *ppl = SSToDS(&pl);
    int                rc = NO_ERROR;

    // Check if segment being loaded already, if so block until loaded then
    // retry. Otherwise take the semaphore flag and load the segment.
    //
    // NOTE !!! This is not the best way to handle this. We block the 2nd
    // fault in this segment to wait for the first to get out of the way.
    // This makes sense on a segment fault, but is crazy on a page fault.
    // Fix for 2.1 ....
    //
    // BUGBUG ???? The 2nd segment fault is blocked until the first segment
    // fault is done. However, the 2nd segment fault then proceeds and loads
    // the segment again ???? The 2nd segment fault needs to exit. This
    // check is made for packed segments but not for regular segments.
    //
    if (pste->ste_flags & STE_SEMAPHORE)
    {
        while (TRUE)
        {
            pste->ste_flags |= STE_WAITING;
            if ( rc = TKSleep( &pste->ste_flags, -1, 0, 0 ) )
                return(rc);
            if ( !(pste->ste_flags & STE_SEMAPHORE) )
                break;
        }
    }

    // Don't load packed segments a second time if they have already been
    // loaded. Packed segments are swappable not discardable, so we can not
    // get into this code on a page fault - we can only get here on a
    // segment fault. So, if we got here on a segment fault, and then find
    // it is present, it must have been because we were blocked in the
    // TKSleep above. Therefore, this segment is now already loaded.
    if ( (pste->ste_flags & (STE_PACKED|STE_PRESENT)) ==
                                                    (STE_PACKED|STE_PRESENT) )
        return( NO_ERROR );

    // Set the semaphore to block any other thread trying to fault this
    // segment.
    pste->ste_flags |= STE_SEMAPHORE;

    // If we are doing a preload, then we need to use the child's PTDA.
    // If we are doing a segment fault, then we must use the current PTDA.
    if (plf->lf_faultaddr == -1)
    {
        // We are doing a preload - the -1 was set in the preload code.
        pptda = TKContext();
        plf->lf_faultaddr = plf->lf_objaddr;
    }
    else
    {
        // Doing a page/segment fault.
        pptda = pPTDACur;
    }

    pmte = plv->lv_pmte;

    // Set object num we are loading. For a segment, the low nybble of the
    // bno is the page number within the segment, the rest of the bno is
    // the segment number within the executable module.
    plv->lv_objnum = plf->lf_bno >> 4;

    //  We do not cache any of the following types of segments:
    //  (1) Segments loaded from fixed media
    //  (2) Segemnts not from a DLL
    //  (3) Shared segments
    //  (4) Segments that do not contain data
    //  (5) GDT segments
    //  (6) Segments with no file data
    fInstCache =    !( pmte->mte_flags1 & MTE_MEDIAFIXED ) // removeable
                 &&  ( pmte->mte_flags1 & LIBRARYMOD )     // is a DLL
                 && !( pste->ste_flags & STE_SHARED )      // instance
                 &&  ( pste->ste_flags & STE_DATA )        // data
                 &&  ( pste->ste_selector & SEL_LDTBIT )   // LDT
                 &&  ( pste->ste_offset != 0);             // data in file

    /*
     * compute number of pages to allocate for this segment.
     */
    cbseg = (ulong_t) RESIZE64K(pste->ste_minsiz);
    npages = ((plf->lf_pallocbuf & PAGEMASK) + cbseg + PAGESIZE-1) >> PAGESHIFT;

    /*
     * If we are called by LDRGetPage, a page has been allocated already
     * so we don't need to allocate a page with PGAllocBuf.
     *
     * If plf->lf_pallocbuf != plf->lf_faultaddr, then we were called
     * by the Page Manager (ie through LDRGetPage).
     *
     * If the segment is pageable, has no iterated data, and there are
     * no relocations, OR we have already loaded the segment, and
     * therefore fixup information exists, then only read one page.
     * Also for segments that are packed don't save the relocation
     * info.
     */

    fPageable =     (    (pste->ste_flags & STE_PAGEABLE)
                      && !(pste->ste_flags & STE_ITERATED)
                      && !(pste->ste_flags & STE_RELOCINFO) )
                || pste->ste_fixups && !(pste->ste_flags & STE_PACKED);

    fnopgbuf =    plf->lf_pallocbuf != plf->lf_faultaddr
               && (    npages == 1
                    || (fPageable && !fInstCache) );

    if (fnopgbuf)
    {
        // Called from the page fault code

        plv->lv_lbufaddr = plf->lf_pallocbuf;
        plv->lv_sbufaddr = plf->lf_sallocbuf;
        npages = 1;
    }
    else
    {
        // Called from segment fault or segment preload code

        fpgflags =     ( (pmte->mte_flags1 & MTE_MEDIACONTIG) ? PG_CONTIG : PG_TRY_CONTIG )
                     | ( (pmte->mte_flags1 & MTE_MEDIA16M) ? PG_16M : 0 )
                     | PG_DISCARDABLE;

        // if segment packed segment
        if (pste->ste_flags & STE_PACKED)
        {
            // Retry loop to lock the pages
            for ( cloops = 0, rc = ERROR_SHORTLOCK;
                  (cloops < 12) && (rc==ERROR_SHORTLOCK);
                  cloops++ )
            {
                // Try do the lock - needed before calling PGLinToPageList
                rc = PGLock( plf->lf_faultaddr >> PAGESHIFT,
                             pptda,
                             VMRegionSize( plf->lf_faultaddr,
                                           cbseg ) >> PAGESHIFT,
                             fpgflags|PG_W|PG_TRUSTED );

                // Sleep delay on short term lock failure
                if (rc == ERROR_SHORTLOCK)
                {
                    TKSleep( (char *)plf->lf_faultaddr, 700, 0, 0 );
                }

            } // end of retry loop

            // Did the lock go ok ?
            if (rc)
            {
#ifdef LDRSTRICT
                if ( rc!=ERROR_INTERRUPT )
                    panic("ldrSegment - bad lock");
#endif
                goto ldrSegmentExit;
            }

            PGLinToPageList( plf->lf_faultaddr,
                             pptda,
                             cbseg,
                             ppl);
            if ( rc = PGAllocBuf( cbseg,
                                  fpgflags | PG_REMAP,
                                  (frame_t) ppl,
                                  SSToDS(&pBuff),
                                  &plv->lv_sbufaddr,
                                  (plf->lf_faultaddr >> PAGESHIFT) ) )
            {
#ifdef LDRSTRICT
                if (    rc!=ERROR_NOT_ENOUGH_MEMORY
                     && rc!=ERROR_INTERRUPT )
                    panic("ldrSegment - bad PGAllocBuf 1");
#endif
                pBuff = 0;
                PGUnlock( plf->lf_faultaddr >> PAGESHIFT,
                          pptda,
                          VMRegionSize( plf->lf_faultaddr, cbseg ) >> PAGESHIFT,
                          0 );
                goto ldrSegmentExit;
            }
            plv->lv_lbufaddr = (ulong_t)pBuff;

        } // end of if segment packed code

        else   // segment not packed
        {

            if ( rc = PGAllocBuf( npages,
                                  fpgflags,
                                  -1,
                                  SSToDS(&pBuff),
                                  &plv->lv_sbufaddr,
                                  (plf->lf_faultaddr >> PAGESHIFT) ) )
            {
#ifdef LDRSTRICT
                if (    rc!=ERROR_NOT_ENOUGH_MEMORY
                     && rc!=ERROR_INTERRUPT )
                    panic("ldrSegment - bad PGAllocBuf 2");
#endif
                pBuff = 0;
                goto ldrSegmentExit;
            }
            plv->lv_lbufaddr = (ulong_t)pBuff;

        } // end of if segment not packed

    } // end of if called from segment fault or segment preload code


    // If the pages are in the cache, use the cache ...
    if (    fInstCache
         && (hobcache = pste->ste_seghdl) != HOBNULL
         && hobcache != LDRMKCACHEOWNER )
    {
        // BUGBUG - check rc
        ldrUseDataCache( hobcache,
                         plv->lv_lbufaddr,
                         ( (plf->lf_bno & 0xF) << PAGESHIFT ),
                         (    plf->lf_pallocbuf == plf->lf_faultaddr
                           || cbseg < PAGESIZE )
                                  ? cbseg
                                  : PAGESIZE );

        // If we have just got the pages out of the cache, set the flag
        // to tell us not to make a cache.
        fInstCache = FALSE;
    }

    else   // the pages are not in the cache
    {
        // If the segment has no fixups or there are fixups already recorded
        // for this segment, only need to read the data for the faulting page.
        if (fPageable && fnopgbuf)
        {
            ushort_t  cbpage;   // residual part of a page
            ushort_t  spages;   // number of pages
            ushort_t  pno;      // faulting page number
            ushort_t  cbread;   // number of bytes to read
            ulong_t   cbclear;  // number of bytes to zero

            spages = (pste->ste_size + (ushort_t) (PAGESIZE - 1)) >> PAGESHIFT;
            spages--;               /* make pages zero based */
            cbpage = pste->ste_size & (ushort_t) PAGEMASK;
            pno = (ushort_t) (plf->lf_bno & 0xf);
            cbread = 0;
                                    /* any data exist for this page */

            if (pste->ste_flags & STE_ITERATED)
            {
                if ( rc = ldrSegFixLoadIterated( pste,
                                                 plv,
                                                 pno,
                                                 (uchar_t *)plv->lv_lbufaddr) )
                {
                    goto ldrSegmentExit;
                }

                cbclear = 0;
            }
            else  if (spages >= pno && pste->ste_offset != 0)
            {

                /*
                 * check for last page of segment.
                 * Make sure that the last isn't equal to a page.
                 */
                if (spages == pno && cbpage != 0)
                {
                    cbread = cbpage;
                    cbclear = (ushort_t) PAGESIZE - cbread;
                }
                else
                {
                    cbread = PAGESIZE;
                    cbclear = 0;
                }

                if (cbread > 0)
                {
                    if ( rc = ldrRead( pmte->mte_sfn,
                                       (pste->ste_offset <<
                                       pmte->mte_swapmte->smte_alignshift) + (pno * PAGESIZE),
                                       (uchar_t *) plv->lv_lbufaddr,
                                       plv->lv_sbufaddr,
                                       cbread,
                                       pmte ) )
                    {
                        goto ldrSegmentExit;
                    }
                }
            }
            else
                cbclear = PAGESIZE;

            if (cbclear > 0)
                memset( (char *)plv->lv_lbufaddr + cbread, 0, cbclear );

            /*
             * If this segment has fixup information, fixup the page
             * using the recorded information
             */
            if (pste->ste_fixups)
            {
                if ( rc = ldrSegFixFixupPage( pste,
                                              (ulong_t)pno,
                                              (uchar_t *)plv->lv_lbufaddr) )
                {
                    goto ldrSegmentExit;
                }
            }
            else
            {

                /*
                 * Use type & class field of local variables to indicate
                 * that ldrSegment is calling ldrEachObjEntry and pass
                 * faulting page number.
                 */

                plv->lv_type = 0xff; /* flag says ldrSegment is calling*/
                plv->lv_class = pno; /* faulting page number */

                rc = ldrEachObjEntry( plv->lv_objnum,
                                      pmte,
                                      ldrEditProLog,
                                      plv );
            }
        }

        else
        {
            /*
             * If not (data and not read only) then record fixups
             */

            if ((pste->ste_flags & (STE_PAGEABLE | STE_PACKED)) == STE_PAGEABLE)
            {
                if ( rc = ldrSegFixOpenRec(pste) )
                {
                    goto ldrSegmentExit;
                    // BUGBUG ???? what about the lock ???? should be freed ?
                }
            }

            // Load and fixup the segment.
            rc = ldrLoadSegment( pste, plv, cbseg );

            if (pste->ste_flags & STE_PACKED)
            {
                PGUnlock( plf->lf_faultaddr >> PAGESHIFT,
                          pptda,
                          VMRegionSize( plf->lf_faultaddr, cbseg ) >> PAGESHIFT,
                          0 );

                /*
                 *  After loading a packed segment, mark it STE_PRESENT
                 *  so we don't try to load it again.
                 */
                if (!rc)
                    pste->ste_flags |= STE_PRESENT;

                goto ldrSegmentExit;

            }

            if (pste->ste_fixups)
                ldrSegFixCloseRec(pste);

            if (rc)    /* If load of segment from file fails */
                goto ldrSegmentExit;

        }

    } // end of if the pages are not in the cache

    // Make the cache if necessary
    if (fInstCache)
        ldrMakeDataCache( pste, plv->lv_lbufaddr, cbseg );

    // Check for Device Driver or FSDs, for DDs with GDT selectors copy
    // data from PGAllocBuf to Kernel resident heap.
    if (    (pmte->mte_flags1 & (DEVDRVMOD | FSDMOD))
         && (pste->ste_flags & STE_GDTSEG) )
    {

        // data segment
        if (pste->ste_flags & STE_DATA)
            TKCopyBuff( plf->lf_pallocbuf, plv->lv_lbufaddr, cbseg );

        else // not data segment
        {
            ULONG   laddr;          /* Copy of plf->lf_pallocbuf */

            ltaddr = plv->lv_lbufaddr;
            laddr  = plf->lf_pallocbuf;

            // If address is not page aligned
            if (laddr & PAGEMASK)
            {
                cb = (ulong_t)(PAGESIZE - (laddr & PAGEMASK));
                cb = cb < cbseg ? cb : cbseg ;
                cbseg -= cb;
                TKCopyBuff( (((PGMapTmpAlias(laddr>>PAGESHIFT)) << PAGESHIFT )
                                                         + (laddr & PAGEMASK) ),
                            ltaddr,
                            cb );
                ltaddr += cb;
                laddr += cb;
            }

            while (cbseg > 0)
            {
                // Touch each page to make present, because it may have
                // been allocated from the swappable heap.
                cbscratch = *((volatile ulong_t *) laddr);

                // Handle page aligned addresses
                if (cbseg <= PAGESIZE)
                {
                    cb = cbseg;
                    cbseg = 0;
                }
                else
                {
                    cb = PAGESIZE;
                    cbseg -= PAGESIZE;
                }
                TKCopyBuff( ( (PGMapTmpAlias(laddr>>PAGESHIFT)) << PAGESHIFT ),
                            ltaddr,
                            cb );
                ltaddr += PAGESIZE;
                laddr += PAGESIZE;
            }

        } // end of not data segment

    } // end of a DD or FSD GDT selector

    // Check to see if all pages need to be moved, this happens when
    // called by LDRGetSegment.  All the pages must be preloaded.
    else if (plf->lf_pallocbuf == plf->lf_faultaddr)
    {
        rc = PGPreload( plv->lv_lbufaddr >> PAGESHIFT,
                        plf->lf_faultaddr >> PAGESHIFT,
                        pptda,
                        npages,
                        -1,
                        !(pmte->mte_flags1 & MTE_MEDIAFIXED) );
#ifdef LDRSTRICT
            if (    rc!=0
                 && rc!=ERROR_INTERRUPT )
                panic("ldrSegment - bad PGPreload 1");
#endif
    }

    else if (!fnopgbuf)
    {
        /*
         * We have demand loaded a segment into the PGAllocBuf buffer and
         * we want to move all these pages into their user-space mapping.
         * Call PGPreload to do this.
         */
        /*
         *  The first page in an alias object may not map the first
         *  page in the segment, so for aliases we compute an offset
         *  into the segment to start the load from.  Since the low
         *  4 bits of the block number are the page offset into the
         *  segment, we have all the info we need.
         *  Because we have no way of knowing how big the alias really
         *  is, just load the one page that was faulted on.
         */
        loadstart = plf->lf_faultaddr;
        if ( plf->lf_faultaddr != plf->lf_objaddr )
        {
            offset = plf->lf_bno & 0x0f;
            npages = 1;
        }
        else
        {
            loadstart &= ~(_64K - 1);
            offset = 0;
        }
        // the pages that were not the faulted on page will go onto the
        // idle list ... performance enhancement could be to not do this,
        // but to let the age anyways
        rc = PGPreload( (plv->lv_lbufaddr >> PAGESHIFT) + offset,
                        loadstart >> PAGESHIFT,
                        pptda,
                        npages,
                        plf->lf_faultaddr >> PAGESHIFT,
                        1);
        if (!rc)
            rc = ERROR_INCACHE;   /* tell page manager we used own frame */
#ifdef LDRSTRICT
        else
        {
            if ( rc!=ERROR_INTERRUPT )
                panic("ldrSegment - bad PGPreload 2");
        }
#endif
    }

ldrSegmentExit:

    // Free allocated pages if any were allocated.
    if (pBuff)
        PGFreeBuf( (laddr_t)pBuff );

    // Release the semaphore and any other threads waiting on this segment.
    if (pste->ste_flags & STE_WAITING)
    {
        TKWakeup( &pste->ste_flags, 0, 0 );
    }
    pste->ste_flags &= ~(STE_SEMAPHORE|STE_WAITING);

    return(rc);

} // end of ldrSegment


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


/***LP  ldrUseDataCache - copy instance data segment from cache.
 *
 *      As per 1.2 DCR 122, instance data segments are cached.  This
 *      routine is called by ldrSegment to attempt to copy data from
 *      an instance data cache to the destination buffer.
 *
 *      ENTRY   hobcache        - cache object handle
 *              pdst            - destination buffer address
 *              off             - offset in segment at which to start
 *              cb              - number of bytes to copy
 *
 *      EXIT    NONE
 */

void ldrUseDataCache( VMHOB   hobcache,  // Cache handle
                      ulong_t pdst,      // Linear address of destination
                      ulong_t off,       // Offset in segment
                      ulong_t cb )       // Number of bytes to copy
{
    ulong_t pcache;         // Cache pointer

    // If not pseudohandle
    if ( VMHandleVerify( hobcache, 1, SSToDS(&pcache) ) )
    {
#ifdef LDRSTRICT
        /* Get cache object address */
        if ( VMGetLaddr( hobcache, SSToDS(&pcache) ) )
            panic("ldrUseDataCache: VMGetLaddr");
#else
        /* Get cache object address */
         VMGetLaddr( hobcache, SSToDS(&pcache) );
#endif
    }

    // Copy from cache to object
    TKCopyBuff( pdst+off, pcache+off, cb );

} // end of ldrUseDataCache




/***LP  ldrMakeDataCache - make instance data cache
 *
 *      As per 1.2 DCR 122, instance data segments are cached.  This
 *      routine is called by ldrSegment.
 *
 *      We cache instance data segments.  The cache is marked
 *      swappable and AOD.  If fixed media, then no cache is made.
 *
 *      ENTRY   pste            - STE pointer
 *              psrc            - pointer to data to cache
 *              cb              - count of bytes of data to cache
 *
 *      EXIT    NONE
 */

void ldrMakeDataCache( ldrste_t *pste,    // Pointer to STE
                       ulong_t   psrc,    // Buffer containing data to cache
                       ulong_t   cb )     // Number of bytes to cache
{
    VMAC     ac;           // VMAllocMem return buffer
    VMHOB    hobcache;     // Handle of cache object
    uchar_t *pbcache;      // Virtual address of cache object

    // Do not yield while changing state
    TKBeginNoBlock();

    // If cache exists or is being made
    if ( (hobcache = pste->ste_seghdl) != HOBNULL )
    {
        TKEndNoBlock();
        return;
    }
    pste->ste_seghdl = LDRMKCACHEOWNER;

    TKEndNoBlock();

    if (cb <= LDRMAXHEAPCACHE)      /* If cache small enough for heap */
    {
        if ( VMAllocKHB( VM_HKH_PUB_SWAPRW,
                         cb,
                         (VMHOB) LDRCACHEOWNER,
                         NA,
                         SSToDS(&pbcache) ) )
        {
#ifdef LDRSTRICT
            panic("ldrMakeDataCache - KHBAlloc");
#endif
        }
        else
        {
            if ( VMCreatePseudoHandle( pbcache,
                                       (VMHOB) LDRCACHEOWNER,
                                       SSToDS(&hobcache) ) )
           {
#ifdef LDRSTRICT
                panic("ldrMakeDataCache - PseudoH");
#endif
                VMFreeKHB( VM_HKH_PUB_SWAPRW, pbcache );
           }
           else
           {
                memcpy( pbcache, (uchar_t *) psrc, cb );
           }
        }
    }
    else                            /* Else allocate object for cache */
    {
        if ( VMAllocMem( cb,
                         cb,
                         VMAC_ARENASYS | PG_R | PG_W | PG_SWAPPABLE,
                         HOBNULL,
                         (VMHOB) LDRCACHEOWNER,
                         HOBNULL,
                         0,
                         0,
                         SSToDS(&ac) ) )
        {
#ifdef LDRSTRICT
            panic("ldrMakeDataCache - VMAlloc");
#endif
        }
        else
        {
            TKCopyBuff( ac.ac_va, psrc, cb );
            hobcache = ac.ac_hob;
        }
    }
    pste->ste_seghdl = hobcache;

} // end of ldrMakeDataCache



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


/***LP  ldr32UnpackPage - expand an iterated page.
 *
 * Unpack an iterated page as packed by LINK386 or by the 32-bit RC.  The data
 * has been replaced by iteration records (structure defined in EXE386.H).
 *
 * If we are called with a Sfn and a FileOffset, then Unpack needs to read the
 * data from the file first.  Otherwise, the data is already in the target
 * page and we need to unpack in place. The former is how we are called for a
 * page fault, the latter is how we are called for a preload.
 *
 * Fail gracefully if something nasty such as corrupt iteration record occurs
 * - we are, after all, in ring 0 processing data read from a user-messable
 * file.  A corrupt file must not result in the loader trapping or scribbling
 * around in memory.
 *
 * We need a temporary work area to unpack/read the data into. If we fail to
 * allocate such a buffer, just return on a preload - this will kill the app
 * being execed, which is goodness in a low memory system. However, if we can
 * not get a a buffer on a page fault, we have to do it anyways - we can not
 * die in a page fault of a running app if at all possible.
 *
 * Returns error code if we couldn't handle it. If this was a page fault,
 * a bad error code will be fatal to the app.
 */

int ldr32UnpackPage( ldrmte_t *    pMTE,       // pMTE for file read, pass thru to ldrRead
                     char *        pPage,      // Pointer to the target page to be unpacked.
                     unsigned long FileOffset, // Offset in the file if read required.
                     ushort_t      Sfn,        // File number if read required.
                     unsigned long cbIn )      // Number of characters in (page size in the file)
{
    struct LX_Iter *pItRec; // Iteration record overlay
    unsigned short i;       // Loop counter
    unsigned long cbOut;    // Check we are not about to fall off the page
    int rc;                 // Keep track of errors.
    VMKHBI KHeap;           // Structure to receive the krnl heap information.
    char * pBuff;           // Pointer to locally allocated buffer


    // Check the number of bytes is a sensible size.
    // Note the 2 is for the loop terminating condition below. Note that
    // neither the linker nor the resource compiler will pack pages to a
    // size of PAGESIZE-2 so this is safe.
    if ( rc = ( cbIn < PAGESIZE-2 ) ? 0 : ERROR_BAD_EXE_FORMAT )
    {
#ifdef LDRSTRICT
        panic("LDR32UnpackPage: too much in");
#endif
        return( rc );
    }

    // Setup a buffer read/unpack into. First, try to get a page.
    if ( rc = PGAllocBuf( 1,
                          PG_16M | PG_DISCARDABLE,
                          -1,                // need physcial storage
                          SSToDS(&pBuff),
                          SSToDS(&pItRec),   // unwanted 16-bit pointer
                          0 ) )
    {
        if ( rc == ERROR_INTERRUPT )
            return( rc );

        // Failed, so try to get storage from the kernel heap instead.
        if ( rc = VMAllocKHB( VM_HKH_PUB_RESRW,
                              cbIn+2,   // +2 for loop terminating condition
                              (VMHOB) LDRCACHEOWNER,
                              0,
                              SSToDS(&KHeap) ) )
        {
            // Failed to get storage from either the page manager or from the
            // kernel heap. If we were called from preload, then just abort.
            if (!FileOffset)
                return(rc);

            if ( rc == ERROR_INTERRUPT )
                return( rc );

            // Zero pointer we know not to clear up storage that we did
            // not allocate.
            pBuff = KHeap.khbi_pbData = 0;
        }
        else
        {
            // Got some storage from the kernel heap - set the pointer to it.
            pBuff = KHeap.khbi_pbData;
        }
    }
    else
    {
        // Succeeded in allocating a buffer from the 10 page pool. Set a flag
        // in the KHeap structure so that we know how to free up storage later.
        KHeap.khbi_pbData = 0;
    }

    // Zero fill the remainder of the page in case there are insufficient
    // iteration records to describe the entire page.
    memset( pPage+cbIn,
            0,
            (ulong_t)(PAGESIZE-cbIn) );

    if (pBuff)
    {
        // We allocated some storage ok, so now get on with the read/unpack.

        // Set up the pointer to the iterated data, ready to loop.
        pItRec = (struct LX_Iter *)pBuff;

        // Do we need to read from the file, or do we just need to unpack ?
        if (FileOffset)
        {
            // Called from page fault - need to read from the file.
            if (rc = ldrRead( Sfn,
                              FileOffset,
                              pBuff,
                              0,        // BUGBUG - should pass 16-bit address
                              cbIn,
                              pMTE ) )
            {
                goto ldr32UnpackPage_error;
            }
        }
        else
        {
            // Called from preload - just need to copy the data as it is
            // already read in.
            memcpy( pItRec,
                    pPage,
                    (ulong_t)cbIn );
        }

        // We now have the iteration records in the buffer, and are ready
        // to unpack them into the page.

        // Set up terminating condition for iter unpack loop
        cbOut = 0;
        *(unsigned short *)( (char *)pItRec + cbIn ) = 0;

        // Loop around processing each iteration record in turn until
        // there are no more left - termination condition is that the
        // rest of the page after all the iteration records is zero filled.
        while (pItRec->LX_nIter)
        {

            // If the pattern is one byte long
            if ( pItRec->LX_nBytes==1 )
            {
                // We could actually move this check out, but since the
                // a one-byte pattern is the prevalent pattern, it is
                // faster if we avoid the multiply.
                // It costs a few bytes, but saves time over the long haul.
                // Check we are not about to overflow the page - abort if so
                if ( ( cbOut += (unsigned long)pItRec->LX_nIter) > PAGESIZE )
                {
                    // Would overflow the page - can not be allowed to happen
#ifdef LDRSTRICT
                    panic("LDR32UnpackPage: 1 too much out");
#endif
                    rc = ERROR_BAD_EXE_FORMAT;
                    goto ldr32UnpackPage_error;
                }

                // This is a special case of the n-byte code, and the n-byte
                // code will actually work for this case, but this case is
                // so much the normal case that it is worth special casing.

                memset( pPage,
                        pItRec->LX_Iterdata,
                        (ulong_t)pItRec->LX_nIter );
                pPage += pItRec->LX_nIter;
                pItRec++;

            }
            else   // pattern is longer than one byte
            {
                // We could actually move this check out, but since the
                // a one-byte pattern is the prevalent pattern, it is
                // faster if we avoid the multiply.
                // It costs a few bytes, but saves time over the long haul.
                // Check we are not about to overflow the page - abort if so
                if ( ( cbOut += ((unsigned long)pItRec->LX_nBytes*(unsigned long)pItRec->LX_nIter) ) > PAGESIZE )
                {
                    // Would overflow the page - can not be allowed to happen
#ifdef LDRSTRICT
                    panic("LDR32UnpackPage: 2 too much out");
#endif
                    rc = ERROR_BAD_EXE_FORMAT;
                    goto ldr32UnpackPage_error;
                }

                // For each iteration, expand
                for ( i=0; i < pItRec->LX_nIter; i++ )
                {

                    memcpy( pPage,
                            (char *)pItRec + 4,
                            pItRec->LX_nBytes );

                    pPage += pItRec->LX_nBytes;
                }

                // Find the next iter record
                pItRec = (struct LX_Iter *)( ((char *)pItRec) + (
                                                     4 + pItRec->LX_nBytes ) );
            }


        } // end of while pItRec->LX_nIter

        // OEM probs ?? TKTCYield();  // yield added for PTR B732115


    }
    else // No storage available - we need to do a slow unpack to survive.
    {

        // No memory to allocate an unpack buffer. We can't just give up
        // as we are in the middle of a page fault - to fail would be
        // fatal to a running application ... the user would just end up
        // with a hard error popup to kill the app. Not nice.
        // So, read straight from the file into the page, iter recs at a
        // time, and loop. Slow, but better than death. Hopefully we will
        // get real real lucky, and the file system buffering will save the
        // day as regards performance.

        unsigned short Bytes, Repeats;
        unsigned long FileBuffer;
        unsigned long WrkFileOffset = FileOffset;

        // Track how much data we write into the page to avoid overflow if
        // the input file is corrupt.
        cbOut = 0;

        while ( WrkFileOffset < FileOffset+cbIn )
        {
            // Read the iteration record header
            if (rc = ldrRead( Sfn,
                              WrkFileOffset,
                              SSToDS(&FileBuffer),
                              0,        // BUGBUG - pass 16-bit address
                              4,        // Just read 4 bytes
                              pMTE ) )
            {
                goto ldr32UnpackPage_error;
            }

            // Move file pointer to beginning of iter data
            WrkFileOffset+=4;

            // How many repeats of how many bytes ?
            Repeats = (unsigned short)FileBuffer;
            Bytes = (unsigned short)(FileBuffer>>16);

            // Copy from the file into the page for each repeat
            for ( i=1;
                  i<=Repeats;
                  i++, pPage+=Bytes )
            {

                // Check we are not going to overflow.
                cbOut += (unsigned long)Bytes;
                if (cbOut>PAGESIZE)
                {

#ifdef LDRSTRICT
                    panic("LDR32UnpackPage(slow): too much out");
#endif

                    rc = ERROR_BAD_EXE_FORMAT;
                    goto ldr32UnpackPage_error;
                }

                if (rc = ldrRead( Sfn,
                                  WrkFileOffset,
                                  pPage,
                                  0,        // BUGBUG - pass 16-bit address
                                  Bytes,
                                  pMTE ) )
                {
                    goto ldr32UnpackPage_error;
                }

            } // end of for each repeated iteration

            // Move file offset to the start of the next iteration record.
            WrkFileOffset+=Bytes;

        } // end of while more iteration records to process

    } // end of no storage for buffer processing code


ldr32UnpackPage_error:

    // All done - free up any storage we allocated.
    if (KHeap.khbi_pbData)
    {
        // We got it from the kernel heap.
        VMFreeKHB( VM_HKH_PUB_RESRW,
                   KHeap.khbi_pbData );
    }
    else if (pBuff)
    {
        // We got it from one of our pool of 10 pages.
        PGFreeBuf( (laddr_t)pBuff );
    }

    return(rc);

} // end of ldr32UnpackPage

#pragma code_seg ()
#pragma code_seg("SWAP32_6","CODE")
/***LP  ldr32UnpackPage2 - expand an iterdata2 page (second algorithm)
 *
 * Unpack a compressed page as packed by LINK386 or by the 32-bit RC.  The data
 * has been replaced compressed data using an alternate
 *
 * If we are called with a Sfn and a FileOffset, then Unpack needs to read the
 * data from the file first.  Otherwise, the data is already in the target
 * page and we need to unpack in place. The former is how we are called for a
 * page fault, the latter is how we are called for a preload.
 *
 * Fail gracefully if something nasty such as corrupt data occurs
 * - we are, after all, in ring 0 processing data read from a user-messable
 * file.  A corrupt file must not result in the loader trapping or scribbling
 * around in memory.
 *
 * We need a temporary work area to unpack/read the data into. If we fail to
 * allocate such a buffer, just return on a preload - this will kill the app
 * being execed, which is goodness in a low memory system.
 * If if we can not get a a buffer on a page fault, we should try to do it
 * anyways - we should not die in a page fault of a running app if at all
 * possible.  Unfortunately, the current alternate decompression algorithm will
 * not allow this.
 *
 * Returns error code if we couldn't handle it. If this was a page fault,
 * a bad error code will be fatal to the app.
 */

int ldr32UnpackPage2( ldrmte_t *    pMTE,       // pMTE for file read, pass thru to ldrRead
                      char *        pPage,      // Pointer to the target page to be unpacked.
                      unsigned long FileOffset, // Offset in the file if read required.
                      ushort_t      Sfn,        // File number if read required.
                      unsigned long cbIn )      // Number of characters in (page size in the file)
{
    char *pLZBuf;           // 16:16 pointer
    int rc;                 // Keep track of errors.
    VMKHBI KHeap;           // Structure to receive the krnl heap information.
    char * pBuff;           // Pointer to locally allocated buffer
    ULONG   bytes_decompressed;
    ljmp_t lj;              //109965


    // Check the number of bytes is a sensible size.
    // Note the 2 is for the loop terminating condition below. Note that
    // neither the linker nor the resource compiler will pack pages to a
    // size of PAGESIZE-2 so this is safe.
    if ( rc = ( cbIn < PAGESIZE-2 ) ? 0 : ERROR_BAD_EXE_FORMAT )
    {
#ifdef LDRSTRICT
        panic("LDR32UnpackPage2: too much in");
#endif
        return( rc );
    }

    // Setup a buffer read/unpack into. First, try to get a page.
    if ( rc = PGAllocBuf( 1,
                          PG_16M | PG_DISCARDABLE,
                          -1,                // need physical storage
                          SSToDS(&pBuff),
                          SSToDS(&pLZBuf),   // unwanted16:16 pointer
                          0 ) )
    {
        if ( rc == ERROR_INTERRUPT )
            return( rc );

        // Failed, so try to get storage from the kernel heap instead.
        if ( rc = VMAllocKHB( VM_HKH_PUB_RESRW,
                              cbIn+2,   // +2 for loop terminating condition
                              (VMHOB) LDRCACHEOWNER,
                              0,
                              SSToDS(&KHeap) ) )
        {
           // Failed to get storage from either the page manager or from the
           // kernel heap. If we were called from preload, then just abort.
       //??if (!FileOffset)
               return(rc);

       //??if ( rc == ERROR_INTERRUPT )
       //??    return( rc );
       //??
       //??// Zero pointer we know not to clear up storage that we did
       //??// not allocate.
       //??pBuff = KHeap.khbi_pbData = 0;
        }
        else
        {
            // Got some storage from the kernel heap - set the pointer to it.
            pBuff = KHeap.khbi_pbData;
        }
    }
    else
    {
        // Succeeded in allocating a buffer from the 10 page pool. Set a flag
        // in the KHeap structure so that we know how to free up storage later.
        KHeap.khbi_pbData = 0;
    }

    // Zero fill the remainder of the page in case there is insufficient
    // data to describe the entire page.
    memset( pPage+cbIn,
            0,
            (ulong_t)(PAGESIZE-cbIn) );

    if (pBuff)
    {
       // Do we need to read from the file, or do we just need to unpack ?
       if (FileOffset)
       {
           // Called from page fault - need to read from the file.
           if (rc = ldrRead( Sfn,
                             FileOffset,
                             pBuff,
                             0,        // BUGBUG - should pass 16-bit address
                             cbIn,
                             pMTE ) )
           {
               goto ldr32UnpackPage2_error;
           }
       }
       else
       {
           // Called from preload - just need to copy the data as it is
           // already read in.
           memcpy( pBuff,
                   pPage,
                   (ulong_t)cbIn );
       }

        // We allocated some storage ok, so now get on with the read/unpack.

        // We now have the compressed data in the buffer, and are ready
        // to unpack it into the page.

       if(TKCatchFault(SSToDS(&lj)) != NO_ERROR) //109965
       {                                         //109965
          ldrStop(0,0);                          //109965
          rc = ERROR_BAD_EXE_FORMAT;             //109965
          goto ldr32UnpackPage2_error;           //109965
       }                                         //109965

       if( !(bytes_decompressed = EP2_32_Decompress(cbIn,    //Size of SrcBuf
                                          (ulong_t)PAGESIZE, //Size of DstBuf
                                          pBuff,             //Source buffer
                                          pPage )))          //Destination Buffer
           {
           rc = ERROR_BAD_EXE_FORMAT;
           TKClrFault();  //109965
           goto ldr32UnpackPage2_error;
           }

       TKClrFault();  //109965

    }
    else // No storage available - we need to do a slow unpack to survive.
    {
//?? //?????????????????? maybe find a way?
//?? //??????????????????

       rc = ERROR_NOT_ENOUGH_MEMORY;
    } // end of no storage for buffer processing code


ldr32UnpackPage2_error:

    // All done - free up any storage we allocated.
    if (KHeap.khbi_pbData)
    {
        // We got it from the kernel heap.
        VMFreeKHB( VM_HKH_PUB_RESRW,
                   KHeap.khbi_pbData );
    }
    else if (pBuff)
    {
        // We got it from one of our pool of 10 pages.
        PGFreeBuf( (laddr_t)pBuff );
    }

    return(rc);

} // end of ldr32UnpackPage2




// End of swappable code
#pragma code_seg()

/*
 *  Start of init code
 */
#pragma code_seg("CINIT_TEXT", "CODE")

/***LP  ldrCopyVDD - Copy data for a VDD object to MVDM heap space
 *
 *      Copy data from the PGAllocBuf to the memory object created by MVDM.
 *      This object is allocated out of swappable/resident heap and could be
 *      read-only/execute-only. We create a temporary alias to copy. This alias
 *      is short lived and stays around till we block. So we need to touch the
 *      target page before we create the alias. The copy is split up into as
 *      many parts as the number of pages it spans.
 *
 *      ENTRY   pchtgt          target address
 *              pchsrc          source address
 *              cchcopy         count of bytes to copy
 *
 *      EXIT    none
 */

void ldrCopyVDD( pchtgt, pchsrc, cchcopy )
register ulong_t pchtgt, pchsrc;
ulong_t          cchcopy;
{
    register ulong_t cb2copy;   /* count of current copy */
    ulong_t          palias;
    ulong_t          dummy;

    while (cchcopy > 0)
    {
        dummy = *((volatile unsigned long*)pchtgt);

        palias = PGMapTmpAlias(pchtgt >> PAGESHIFT) << PAGESHIFT;

        cb2copy = PAGESIZE - (pchtgt & PAGEMASK);

        if (cb2copy > cchcopy)
            cb2copy = cchcopy;

        memcpy( (void *) (palias + (pchtgt & PAGEMASK)),
                (void *) pchsrc,
                cb2copy );

        cchcopy -= cb2copy;
        pchsrc  += cb2copy;
        pchtgt  += cb2copy;
    }

    return;

} // end of ldrCopyVDD


/***LP  ldrResetDD - Zero fill pages for a PDD or VDD object
 *
 */

void ldrResetDD( pchtgt, cchcopy )
register ulong_t pchtgt;
ulong_t          cchcopy;
{
    register ulong_t cb2copy;   /* count of current copy */
    ulong_t          palias;
    ulong_t          dummy;

    while (cchcopy > 0)
    {
        dummy = *((volatile unsigned long*)pchtgt);

        palias = PGMapTmpAlias(pchtgt >> PAGESHIFT) << PAGESHIFT;

        cb2copy = PAGESIZE - (pchtgt & PAGEMASK);

        if (cb2copy > cchcopy)
            cb2copy = cchcopy;

        memset( (void *) (palias + (pchtgt & PAGEMASK)),
                '\0',
                cb2copy );

        cchcopy -= cb2copy;
        pchtgt  += cb2copy;
    }

    return;

}

// End of init code
#pragma code_seg()



