/*static char *SCCSID = "@(#)ldrfixs.c  6.5 92/02/09";*/
#define SCCSID "@(#)ldrfixs.c   6.5 92/02/09"


/***    OS/2 segment fixup routines
 *
 *  IBM/Microsoft Confidential
 *
 *  Copyright (c) IBM Corporation 1987, 1989
 *  Copyright (c) Microsoft Corporation 1987, 1989
 *
 *  All Rights Reserved
 *
 *  These routines handle the saving of fixup information
 *  for segments. These allow the saving of fixup information
 *  so we can page the segment data, rather than loading
 *  the entire segment.
 *
 *  The basic mechanism for saving fixups works as follows:
 *
 *
 *  The first time a segment is loaded, determine if the
 *  segment should have fixups saved. Segments that qualify for
 *  this are read-only segments (data and code) being read from
 *  fixed media. If fixups are to be recorded, then
 *  ldrSegFixOpenRec is called by ldrSegment to allocate a
 *  block of memory to be used as a header to the fixup
 *  records. This header contains information like pointers to
 *  fixup records on a per-page basis, 'delayed' fixup records,
 *  and iterated data records. A pointer to this block is saved
 *  in the ste for the segment in a field named ste_fixups.
 *
 *  As each fixup record is processed by dofixups,
 *  ldrSegFixAddRecord is called to add a fixup record.
 *  ldrSegFixAddRecord determines which page the fixup is
 *  belongs on, and adds a fixup record to the buffer for that
 *  page. On the first fixup record for the page, a block of
 *  memory is allocated to hold the fixup records. If the fixup
 *  is a chain, then ldrSegFixRecord creates a chain record. A
 *  chain record contains the fixup value, its length, the
 *  number of items in the chain, and is followed by a list of
 *  page offsets which recieve the value.
 *
 *  After each fixup chain is processed by dofixups,
 *  ldrSegFixStopChain is called to complete the chain record.
 *
 *  If a fixup crosses a page boundary, then it is saved in the
 *  delayed fixup section for processing later. This is done so
 *  that a chain will not be broken due to a boundary being
 *  crossed.
 *
 *  When the segment has been loaded and fixed, ldrEditProlog is
 *  called to check for prolog edits. If any are required, they
 *  will be recorded just as a normal fixup.
 *
 *  When all the fixups for a segment have been
 *  recorded, a data structure that looks like below will have
 *  been generated
 *
 *  Segment Table Entry
 *  +---------------+
 *  |ste_fixups |               +------>+---------------+
 *  +---------------+           |       |Fixup records..|
 *      |                       |       +---------------+
 *      |                       |
 *      v sfcb_s                |
 *      +-------+               |
 *      |Page0  |---------------+
 *      +-------+
 *      |Page1  |
 *      +-------+
 *      |Page2  |-----------------------+---------------+
 *      +-------+                       |Fixup records..|
 *      |       |                       +---------------+
 *      +-------+
 *        ...
 *      +-------+
 *      |Page15 |
 *      +-------+ <------- Delayed Fixup Section
 *      |Page0  |
 *      +-------+
 *      |       |
 *      +-------+
 *
 *  When all fixups have been recorded, ldrSegment will call
 *  ldrSegFixCloseRec, which will perform the following steps:
 *
 *      - Process all delayed fixups
 *      - Determine the size of a single buffer large enough
 *              to hold the headers, plus all the fixup records
 *      - Allocate on large buffer
 *      - Condense all fixup records into the single buffer
 *      - Deallocate all smaller temporary buffers
 *
 *  When ldrSegFixCloseRec is done, the above data structure will have
 *  been changed to look like:
 *
 *  Segment Table Entry
 *  +---------------+
 *  |ste_fixups |
 *  +---------------+
 *      |
 *      |
 *      v
 *      +-------+      --
 *      |Page0  |-+     |
 *      +-------+ |     |
 *      |Page1  |-|--+  |
 *      +-------+ |  |  | Offsets into data area for each page
 *      |Page2  | |  |  |
 *      +-------+ |  |  |
 *      |Page3  | |  |  |
 *      +-------+<+  | --
 *      |       |    |
 *      |       |    |
 *      |       |    |
 *      |       |    |  Data area
 *      |       |    |
 *      |       |    |
 *      +-------+<---+
 *      |       |
 *      |       |
 *      |       |
 *      |       |
 *      |       |
 *      |       |
 *      |       |
 *      +-------+
 *      |       |
 *      |       |
 *         ...
 *
 *
 *
 *
 *
 *  Once fixups have been recorded, each call to ldrSegment
 *  will read in only one page of data from the disk. It then
 *  calls ldrSegFixFixupPage to peform fixups on the single
 *  page.
 *
 *  Iterated data is a simple extention to the fixup recording.
 *  As each iterated bundle is expanded, a call to
 *  ldrSegFixAddIterRec is made to record the file offset,
 *  segment offset, data size, and iteration count. If the data
 *  size is 4 bytes or less, then the file offset contains the
 *  actual data, so it will not have to hit the disk.
 *
 *  When an iterated segment is touched the second time,
 *  ldrSegment will call ldrSegFixLoadIterated, which will load
 *  a single page with the appropriate data.
 *
 */

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

#define FNO_NUM         FNO_LDRFIXS_C

#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 <setjmp.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 <stdlib.h>
#include <string.h>


#define SEGFIX_INITSIZE     128         /* Initial buffer size in bytes */
#define SEGFIX_INCRSIZE     128         /* Increment for buffer size */
#define SEGFIX_ITERINCR     8           /* Iterated buffer incr in records */

/*
 * sfbhead_s holds per-page pointers into fixup data blocks
 */
typedef struct sfbhead_s {
    ulong_t pBase;         /* Pointer to heap object */
    sfr_t   *pCurrent;     /* Current fixup frame */
    ulong_t pNextByte;     /* Next available byte */
    ushort_t cbBase;       /* Length of heap object */
}sfbhead_t;

/*
 * Each ste_fixup points to a sfcb_s during creation of fixup records
 */
typedef struct sfcb_s {         /* Segment fixup creation block */

    sfbhead_t sfcb_aBlocks[PAGESPERSEGMENT];    /* Fixup records */
    sfr_t sfcb_aDelayed[PAGESPERSEGMENT];       /* Delayed fixups */
    sfir_t *sfcb_iterated;                      /* Iterated data records */
    sfir_t *sfcb_nextiter;                      /* Next available record */
    ulong_t sfcb_cbiterated;                    /* Bytes allocated */
} sfcb_t;


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


int ldrSegFixAddRecord(ldrste_t *, ulong_t, ulong_t, uchar_t *, ulong_t);
int ldrSegFixStopChain(ldrste_t *);
int ldrSegFixAddIterRec(ldrste_t *, ulong_t, ulong_t, ulong_t, ulong_t);
int segfix_FreeTempFixups(ldrste_t *);
int segfix_ReallocBlock(sfbhead_t *, ldrste_t *);
int ReadIterations(ldrmte_t *,sfir_t *,void *,ulong_t,ulong_t,ulong_t);
// extern int ldrByteReplicate(void *,void *,ulong_t);


/**LP   ldrSegFixOpenRec - Initialize for recording of fixup information
 *
 *  This procedure is called by ldrSegment to initialize the data structures
 *  needed to record fixup information for a segment.
 *
 *  ENTRY   pste        Pointer to segment table entry
 *
 *  EXIT    int         return code (NO_ERROR if successful)
 *                      pste->fixup == pointer to a sfcb_s
 *
 */

int ldrSegFixOpenRec(pste)
register ldrste_t *pste;
{
    int rc;
    ulong_t pTemp;

#ifdef LDRSTRICT
    ldrAssert(!pste->ste_fixups);
#endif

   /*
    * Allocate table memory
    */

    if ((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, sizeof(struct sfcb_s),
                         (VMHOB) LDRMTEOWNER, NA, SSToDS(&pTemp))) != NO_ERROR)
    {
        return rc;
    }

    memset((void *)pTemp, 0, sizeof(struct sfcb_s));

    pste->ste_fixups = pTemp;


    return NO_ERROR;
}

#define CalcFixupOverrun(bp, len) \
   (((bp)->pNextByte + (len) - (ulong_t)(bp)->pBase) > (bp)->cbBase)

/***LP ldrSegFixAddRecord   Add a fixup record to buffer
 *
 *  This routine will add a fixup record to the ste_fixups buffer. It will
 *  perform the following functions
 *
 *  if needed, allocate a block for this page
 *  if fixup crosses page boundary,
 *      delay recording until later
 *
 *
 *  ENTRY   pste        Pointer to segment table entry
 *          ulOffset    offset into segment for fixup
 *          ulSize      Number of bytes in fixup
 *          pbData      Pointer to data for fixup
 *          fChained    1 = Expect chained data
 *                      0 = Single fixup
 *
 *  EXIT    int         return code (NO_ERROR if successful)
 *                      record added to list
 *
 */

int ldrSegFixAddRecord(pste, ulOffset, ulSize, pbData, fChained)
ldrste_t *pste;
ulong_t ulOffset;
ulong_t ulSize;
uchar_t *pbData;
ulong_t fChained;
{

    register sfbhead_t *psfbh = (void *) pste->ste_fixups;
    register sfr_t *psfr;
    ulong_t rc;
    ulong_t ulPageOffset = ulOffset & PAGEMASK;
    ulong_t ulPageNum = ulOffset >> PAGESHIFT;
    void *pTmp;

    /*
     * Records only work on CODE without iterated data
     */

#ifdef LDRSTRICT
    ldrAssert(pste->ste_fixups);
    ldrAssert(psfbh);
    ldrAssert(ulPageNum < PAGESPERSEGMENT);
#endif

    pTmp = (void *)&(((sfcb_t *)psfbh)->sfcb_aBlocks[ulPageNum]);
    psfbh = (sfbhead_t *)pTmp;

    if (!psfbh->pBase)           /* Do we need to allocate a block? */
    {
       if ((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, SEGFIX_INITSIZE,
            (VMHOB) LDRMTEOWNER, NA, &psfbh->pBase)) != NO_ERROR)
       {
          /* If an error occurred, then we cannot fixup this
           * segment. We should free any memory associated with
           * the fixups for this segment.
           */
          segfix_FreeTempFixups(pste);

          return rc;
       }
       psfbh->pCurrent = (void *)psfbh->pNextByte = (void *)psfbh->pBase;
       psfbh->cbBase = SEGFIX_INITSIZE;
    }


    /*
     * If this fixup will cross a page boundary, then skip it for now,
     * and we will come back to it later. We will keep track of it by
     * placing it in the delayed record buffer for the current page.
     *
     */
    if ((ulPageOffset + ulSize - 1) & ~PAGEMASK)
    {
       psfr = &((sfcb_t *)(pste->ste_fixups))->sfcb_aDelayed[ulPageNum];
       goto PushRecord;
    }

    /*
     * Check to see if we need to push a new record on top of the block
     */
    psfr = psfbh->pCurrent;

    if ((void *)psfbh->pNextByte == (void *)psfbh->pCurrent)
    {
       if (CalcFixupOverrun(psfbh, SFR_HEADSIZE + ulSize))
       {
          if (rc = segfix_ReallocBlock(psfbh, pste))
              return rc;
          psfr = psfbh->pCurrent;
       }

       psfbh->pNextByte = (ulong_t)psfr + SFR_HEADSIZE + ulSize;

PushRecord:

       psfr->sfr_fchained = 0;
       psfr->sfr_soff = (ushort_t) ulPageOffset;
       psfr->sfr_fixsize = (ushort_t) ulSize;

       memcpy((void *)&(psfr->sfr_bytes), (void *)pbData, ulSize);

       goto AddRecExit;
    }

    /*
     * If currently not chained, then make it chained
     */
    if (!psfr->sfr_fchained)
    {
       // Check for overrun
       if (CalcFixupOverrun(psfbh, sizeof(ushort_t)))
       {
          if (rc = segfix_ReallocBlock(psfbh, pste))
              return rc;
          psfr = psfbh->pCurrent;
       }

       *((ushort_t *)psfbh->pNextByte) = psfr->sfr_soff;

       psfbh->pNextByte+=sizeof(ushort_t);

       psfr->sfr_soff = 1;     /* soff is now a counter of chain records */

       psfr->sfr_fchained = 1;
    }

    if (CalcFixupOverrun(psfbh, sizeof(ushort_t)))
    {
       if (rc = segfix_ReallocBlock(psfbh, pste))
           return rc;
       psfr = psfbh->pCurrent;
    }

    *((ushort_t *)psfbh->pNextByte) = (ushort_t)ulPageOffset;

    psfbh->pNextByte  +=  sizeof(ushort_t);
    psfr->sfr_soff += 1;

AddRecExit:

    if (!fChained) (ulong_t)psfbh->pCurrent = (ulong_t)psfbh->pNextByte;

    return NO_ERROR;

}

/***LP ldrSegFixStopChain
 *
 * At the end of each chain of records, we need to reset pCurrent to
 * equal pNext in each fixup block.
 *
 * ENTRY        pste    Segment table entry for segment being fixed
 *
 * EXIT         int     always returns NO_ERROR
 *
 *
 */
int ldrSegFixStopChain(pste)
ldrste_t *pste;
{
    register sfbhead_t *psfbh = (void *)pste->ste_fixups;
    register int i;

    if (!psfbh) return NO_ERROR;

    for(i = 0;i<PAGESPERSEGMENT;i++, psfbh++)
        (ulong_t)psfbh->pCurrent = (ulong_t)psfbh->pNextByte;
    return NO_ERROR;
}



/***LP ldrSegFixCloseRec
 *
 *
 * This routine is called when all fixups for the pste have been
 * recorded. This routine will:
 *
 *  - handle any delayed records and
 *    total size needed to store fixup records,
 *  - Allocate a single heap object to store fixup information
 *  - Copy temporary fixup records from each storage block into
 *    the single fixup record block
 *  - Free the temporary fixup records
 *
 *  ENTRY       pste            Segment table
 *
 *  EXIT        int             return code (NO_ERROR if successful)
 */
int ldrSegFixCloseRec(pste)
ldrste_t *pste;
{
    register sfbhead_t *psfbh = (void *)pste->ste_fixups;
    register sfr_t *psfr;
    ulong_t rc;
    ulong_t i;
    ulong_t temp;
    ulong_t ulPagesInSegment;
    ulong_t *pulBigBuf;
    ulong_t totalsize = 0;
    ulong_t headersize = 0;
    ulong_t itersize = 0;

    char    *pNextByte;


#ifdef LDRSTRICT
    ldrAssert(pste->ste_fixups);
#endif

    if (!pste->ste_fixups) return NO_ERROR;

    /*
     * Handle any delayed fixups, and calculate the total size of the
     * fixup buffers
     */
    ulPagesInSegment = (pste->ste_minsiz + PAGESIZE-1)/PAGESIZE;

    psfr = ((sfcb_t *)psfbh)->sfcb_aDelayed;

    for (i = 0; i < ulPagesInSegment; i++)
    {
        if (*(ulong_t *)psfr)         /* If delayed fixups exist */
        {
            temp = PAGESIZE - psfr->sfr_soff;

            if (rc = ldrSegFixAddRecord(pste, (PAGESIZE*i) + psfr->sfr_soff,
                                        temp, (void *)&psfr->sfr_bytes, 0))
                return rc;

            if (rc = ldrSegFixAddRecord(pste, (PAGESIZE*(i + 1)),
                                        psfr->sfr_fixsize - temp,
                                        &psfr->sfr_bytes[temp], 0))
                return rc;
        }

        /* This MUST follow the delayed fixups, since AddRecord may increase
         * the size of the current page.
         */
        totalsize += psfbh[i].pNextByte - psfbh[i].pBase;
        psfr++;
    }


    /*
     * Adjust for index pointers plus one at the end
     */
    headersize = (ulPagesInSegment + 1)*sizeof(ulong_t);

    /*
     * If iterated data, then make room for iterated data records
     */

    if (pste->ste_flags & STE_ITERATED)
    {
        headersize += sizeof(ulong_t);
        totalsize +=  itersize =
                      (ulong_t)((sfcb_t *)psfbh)->sfcb_nextiter-
                      (ulong_t)((sfcb_t *)psfbh)->sfcb_iterated;
    }

    /*
     * Allocate a buffer to hold all fixup information.
     */
    if ((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, totalsize+headersize,
         (VMHOB) LDRMTEOWNER, NA, SSToDS(&pulBigBuf))) != NO_ERROR)
    {
        segfix_FreeTempFixups(pste);
        return rc;
    }

    /*
     * Preset pointers into large buffer
     */
    (ulong_t)pNextByte = pulBigBuf[0] = (ulong_t)pulBigBuf + headersize;

    /*
     * Concat each fixup buffer into large buffer. Free each buffer as we
     * finish with it.
     */
    for (i = 0;i<ulPagesInSegment;i++, psfbh++)
    {
        if (psfbh->pBase)
        {
            memcpy(pNextByte, (void *)psfbh->pBase,
                   psfbh->pNextByte - psfbh->pBase);

            pNextByte += (ulong_t)(psfbh->pNextByte - psfbh->pBase);

            VMFreeKHB(VM_HKH_PUB_SWAPRW, (void *) psfbh->pBase);
            psfbh->pBase = NULL;
        }
        pulBigBuf[i + 1] = (ulong_t)pNextByte;
    }
    if (pste->ste_flags&STE_ITERATED)
    {
        psfbh = (void *)pste->ste_fixups;
        memcpy(pNextByte, ((sfcb_t *)psfbh)->sfcb_iterated, itersize);

        pulBigBuf[ulPagesInSegment + 1] = (ulong_t)pNextByte;
        VMFreeKHB(VM_HKH_PUB_SWAPRW, ((sfcb_t *)psfbh)->sfcb_iterated);
    }
    VMFreeKHB(VM_HKH_PUB_SWAPRW, (void *) pste->ste_fixups);

    pste->ste_fixups = (ulong_t)pulBigBuf;

    return NO_ERROR;
}


/*** segfix_ReallocBlock
 *
 * Reallocates a temporary fixup block by SEGFIX_INCRSIZE
 *
 * ENTRY        psfbh     Points to block header to realloc
 *              pste      ste containing psfbh
 *
 * EXIT         Returns NO_ERROR if realloc successful
 *    If VMRealloc fails,
 *      frees temp fixup buffer
 *      returns error code from VMRealloc
 */
int segfix_ReallocBlock(psfbh, pste)
sfbhead_t *psfbh;
ldrste_t *pste;
{
    int rc;
    ulong_t ulOldBase = psfbh->pBase;

#ifdef LDRSTRICT
    ldrAssert(ulOldBase);
#endif

    /*
     * Save delta for Current and NextByte pointers
     *
     */

    (ulong_t)psfbh->pCurrent -= (ulong_t)psfbh->pBase;
    (ulong_t)psfbh->pNextByte -=(ulong_t)psfbh->pBase;

    /*
     * @DB76175 - We need to make sure that a corrupt DLL does not cause
     *            us to overflow the size of our block!. Therefore we
     *            check for overflow before we do the realloc and
     *            return an error on overflow.
     */
    if ((psfbh->cbBase += SEGFIX_INCRSIZE) < SEGFIX_INCRSIZE)
    {
        segfix_FreeTempFixups(pste);
        return ERROR_RELOC_CHAIN_XEEDS_SEGLIM;
    }
    /*
     * @DB76175: End of Insertion
     */

    if (rc = VMReallocKHB(VM_HKH_PUB_SWAPRW, &psfbh->pBase,
        psfbh->cbBase, NA))                                       /* @DB76175 */
    {
        psfbh->pBase = ulOldBase;
        segfix_FreeTempFixups(pste);
        return rc;
    }
    /*
     * Restore delta for Current and NextByte pointers
     */
    (ulong_t)psfbh->pCurrent +=  (ulong_t)psfbh->pBase;
    (ulong_t)psfbh->pNextByte +=(ulong_t)psfbh->pBase;

    return NO_ERROR;
}

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

/** segfix_FreeTempFixups
 *
 *  Free all temporary fixup information from pste->fixups
 *
 *  ENTRY  pste    segment table entry
 *
 *  EXIT   pste->fixups = NULL
 *
 */
int segfix_FreeTempFixups(pste)
ldrste_t *pste;
{
    int i;
    sfbhead_t *psfbh;


    psfbh = (sfbhead_t *)pste->ste_fixups;
    if (!psfbh) return NO_ERROR;

    if (((sfcb_t *)psfbh)->sfcb_iterated)
    {
        VMFreeKHB(VM_HKH_PUB_SWAPRW, ((sfcb_t *)psfbh)->sfcb_iterated);
    }

    for (i = 0;i<PAGESPERSEGMENT;i++, psfbh++)
    {
        if (psfbh->pBase)
            VMFreeKHB(VM_HKH_PUB_SWAPRW, (void *) psfbh->pBase);
    }

    VMFreeKHB(VM_HKH_PUB_SWAPRW, (void *) pste->ste_fixups);
    pste->ste_fixups = NULL;                                      /* @DB76175 */

    return NO_ERROR;
}



/**LP ldrSegFixAddIterRec -    Add iterated data record to fixup
 *
 * This routine saves the iteration record header on the end of a
 * buffer. The following steps are performed:
 *
 * If needed, allocate a block of memory for the buffer
 * If needed, grow the buffer
 * Store iteration record at end of buffer
 *
 *
 * ENTRY        pste            Segment table entry
 *              uldoff          File offset
 *              ulsoff          Segment offset
 *              ulsize          Data size
 *              uliters         Iteration count
 *
 * EXIT         int             return code (NO_ERROR if successful)
 *
 */
int ldrSegFixAddIterRec(pste, uldoff, ulsoff, ulsize, uliters)
ldrste_t *pste;
ulong_t uldoff;
ulong_t ulsoff;
ulong_t ulsize;
ulong_t uliters;

{
    register sfcb_t *psfcb = (void *)pste->ste_fixups;
    register sfir_t *psfir;
    ulong_t rc;

#ifdef LDRSTRICT
    ldrAssert(pste->ste_fixups);
#endif

    if (!psfcb) return NO_ERROR;

    psfir = psfcb->sfcb_nextiter;

    /*
     * Do we need to allocate a block of memory?
     */
    if (!psfcb->sfcb_cbiterated)
    {
        if ((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW,
                             sizeof(sfir_t) * SEGFIX_ITERINCR,
                             (VMHOB) LDRMTEOWNER, NA,
                             &psfcb->sfcb_iterated)) != NO_ERROR)
        {
            segfix_FreeTempFixups(pste);
            return rc;
        }

        psfir = psfcb->sfcb_nextiter = psfcb->sfcb_iterated;

        psfcb->sfcb_cbiterated = SEGFIX_ITERINCR*sizeof(sfir_t);
    }

    /*
     * Do we need to realloc the current block of memory?
     */
    if (((ulong_t)psfir - (ulong_t)psfcb->sfcb_iterated) >=
        psfcb->sfcb_cbiterated)
    {
        ulong_t ulOldBase =  (ulong_t) psfcb->sfcb_iterated;

        (ulong_t)psfcb->sfcb_nextiter -= (ulong_t)psfcb->sfcb_iterated;

        if (rc = VMReallocKHB(VM_HKH_PUB_SWAPRW, &psfcb->sfcb_iterated,
                              psfcb->sfcb_cbiterated +=
                              (SEGFIX_ITERINCR*sizeof(sfir_t)), NA))
        {
            psfcb->sfcb_iterated = (void *) ulOldBase;
            segfix_FreeTempFixups(pste);
            return rc;
        }

        (ulong_t) psfir = (ulong_t)psfcb->sfcb_nextiter
                       += (ulong_t)psfcb->sfcb_iterated;
    }

    psfir->sfir_doffset = uldoff;
    psfir->sfir_soffset = (ushort_t) ulsoff;
    psfir->sfir_cbdata  = (ushort_t) ulsize;
    psfir->sfir_count   = (ushort_t) uliters;
    psfcb->sfcb_nextiter = ++psfir;

    return NO_ERROR;
}




/**LP ldrSegFixLoadIterate -  Load an iterated page
 *
 *  This routine will page in a segment that has iterated data.
 *
 *  ENTRY       pste    Segment table entry
 *              plv     Local Vars
 *              ulPage  Page number within segment
 *              pchPage Page to be fixed
 *
 *  EXIT        int     returns NO_ERROR if successful
 *                      otherwise returns ldrRead error
 *
 */

int ldrSegFixLoadIterated(pste, plv, ulPage, pchPage)
ldrste_t *pste;
ldrlv_t *plv;
ulong_t ulPage;
uchar_t *pchPage;
{
    ulong_t soff = (ulPage << PAGESHIFT);
    register sfir_t *psfir;
    ulong_t rc = (pste->ste_minsiz + PAGESIZE - 1)/PAGESIZE + 1;
    ulong_t ulleft = PAGESIZE;          /* Bytes left to read */
    ulong_t ulneeded=0;
    ulong_t ulexpsize;
    uchar_t *pchNext = pchPage;
    ulong_t temp;
    ulong_t ulskip;
    ulong_t ulOddData;
    ldrmte_t *pmte = plv->lv_pmte;


    psfir = (void *) ((ulong_t *)pste->ste_fixups)[rc];

    /*
     * Search down the list of iteration records looking for the first
     * record that will touch this page.
     */
    while ((psfir->sfir_soffset +
           (ulexpsize = psfir->sfir_cbdata*psfir->sfir_count)) < soff)
    {
        psfir++;
    }

    /*
     * cbdata is only zero in the sentinel record
     */
    if (psfir->sfir_cbdata == 0) goto zerofill;


    /*
     * ulskip is the number of bytes between start of record and start of
     * the page to be fixed.
     * If page aligned (ulskip == 0) , skip to iteration loop
     */
    if ((ulskip = soff - psfir->sfir_soffset) == 0)
        goto PageAlignedLabel;        /* ulneeded was initialized to be zero */

    /*
     * ulneeded is the number of bytes to iterate from this record.
     */

    ulneeded = min(ulexpsize - ulskip, PAGESIZE);

    /*
     * Setup for a copy operation
     *
     * If there is only one byte of data,  then this will be very easy
     * since the data is cached,  and the iterations are easy to calculate
     * This is a common case,  so make it fast.
     */

    if (psfir->sfir_cbdata == 1)
    {
        memset(pchNext, (uchar_t)psfir->sfir_doffset,ulneeded);

    }
    else
       if (psfir->sfir_count == 1 ||
           (ulexpsize - ulskip) <= psfir->sfir_cbdata ||
           (psfir->sfir_cbdata - ulskip) >= ulneeded)
       {
           /*
            * If there is one iteration, or one iteration of a large item
            * that spans the entire page, or the last iteration of this
            * fixup, only need one read.
            */

           ulskip = ulskip % psfir->sfir_cbdata;

           if (rc = ReadIterations(pmte, psfir, pchNext, 1, ulskip, ulneeded))
               return(rc);

       }
       else
          if (psfir->sfir_cbdata + ulskip <= ulleft)
          {
              /*
               * Here we have determined that there will be at least one full
               * iteration before the end of the page. Therefore, we can read
               * the full data from the disk, and copy what we need back to
               * where the fixup starts.
               */

               /* temp = # of full iterations after start of page */

               temp = psfir->sfir_count - (ulskip / psfir->sfir_cbdata);

               ulOddData = psfir->sfir_cbdata - (ulskip % psfir->sfir_cbdata);

               ulneeded = min(temp * psfir->sfir_count, ulleft - ulOddData);

               if (rc = ReadIterations(pmte, psfir, pchNext, temp, 0, ulneeded))
                   return(rc);

               memcpy(pchNext,pchNext+ulOddData,ulOddData);

               ulneeded += ulOddData;

          }
          else
          {
               /*
                * Here, we need to issue two partial reads, because we will
                * never have a contigous copy of the data in memory.
                */

               ulOddData = psfir->sfir_cbdata - (ulskip % psfir->sfir_cbdata);

               ulskip = ulskip % psfir->sfir_cbdata;

               if (rc = ReadIterations(pmte, psfir, pchNext, 1,
                                       ulskip, ulOddData))
                  return(rc);

               ulleft -= ulOddData;

               if (rc = ReadIterations(pmte, psfir, pchNext + ulOddData,
                                       1, 0,ulleft))
                   return(rc);
               /*
                * This case will always complete the load. We can go back now.
                */

               return NO_ERROR;
          }

    /*
     * For each record contained on this page, iterate its data
     */

    while (psfir++ && (ulleft-=ulneeded))
    {
        /*
         * Check for sentinel
         */

        if (psfir->sfir_cbdata == 0) goto zerofill;


PageAlignedLabel:

        (ulong_t)pchNext += ulneeded;

#ifdef LDRSTRICT
        soff += ulneeded;
        ldrAssert(psfir->sfir_soffset == (ushort_t) soff);
#endif
        ulneeded = min((ulong_t) (psfir->sfir_count * psfir->sfir_cbdata),
                        ulleft);

        if (rc = ReadIterations(pmte, psfir, pchNext,
                                psfir->sfir_count,0,ulneeded))
            return(rc);
    }

    return NO_ERROR;

zerofill:

    memset(pchNext, 0, ulleft);

    return NO_ERROR;

}



/** ReadIterations
 *
 * This worker routine will read and iterate data
 * for ldrSegFixLoadIterated
 *
 *      ENTRY   pmte            Pointer to mte
 *              pchPage         Pointer to buffer for data
 *              uliters         Count of iterations
 *              ulbias          Bias against start of data
 *              ulmaxread       Maximum number of bytes to iterate
 *
 *      EXIT    int             NO_ERROR on success.
 *
 *      NOTES
 *
 *      ulBias is used to compensate for the case where the current
 *      fixup record starts before the page being fixed up. If ulbias
 *      is > 0, the uliters should be 1.
 */

int ReadIterations(pmte,psfir,pchPage,uliters,ulbias,ulmaxread)
ldrmte_t                *pmte;
register sfir_t         *psfir;
register uchar_t        *pchPage;
ulong_t                 uliters;
ulong_t                 ulbias;
ulong_t                 ulmaxread;
{
    ulong_t cbread;
    ulong_t rc;
    uchar_t *pchNext = pchPage;

    cbread = min((ulong_t) psfir->sfir_cbdata, ulmaxread);

    /*
     * Is the data cached?
     */

    if (psfir->sfir_cbdata <= 4 )
    {
        memcpy(pchPage,(void *)((ulong_t)(&psfir->sfir_doffset)+ulbias), cbread);

    }
    else
    {
        if ( rc = ldrRead( pmte->mte_sfn,
                           (ulong_t)psfir->sfir_doffset + ulbias,
                           pchPage,
                           0,
                           cbread,
                           pmte ) )
            return(rc);
    }

    if (--uliters)
    {
        (ulong_t)pchNext += cbread;

        cbread = min(psfir->sfir_cbdata * uliters, ulmaxread - cbread);

        /*
         * Replicate the pattern for cbread bytes
         */

        // ldrByteReplicate(pchNext,pchPage,cbread);
        memcpy( pchPage, pchNext, cbread );
    }

    return NO_ERROR;

}



/**LP  ldrSegFixFixupPage - Fixup a single page from segment
 *
 *  Apply fixups to a page based on the saved fixup records.
 *
 *  ENTRY       pste    segment table entry
 *              ulPage  Page number within segment
 *              pchPage Linear address of page to be fixed
 *
 *  EXIT        int     NO_ERROR - cannot fail
 *
 */

int ldrSegFixFixupPage(pste, ulPage, pchPage)
ldrste_t *pste;
ulong_t ulPage;
uchar_t *pchPage;
{
    sfr_t *psfr = (sfr_t *) pste->ste_fixups;
    ushort_t *pOffset;                  /* Points to next chain offset */
    int ulCount;                        /* # records in chain */
    int ulSize;                         /* Size of fixup */
    uchar_t *pEnd;                      /* End of fixups */
    register uchar_t *pData;            /* Points to fixup data */
    register uchar_t *temp;


#ifdef LDRSTRICT
    ldrAssert(psfr);
    ldrAssert(ulPage < PAGESPERSEGMENT);
    ldrAssert(ulPage <= (((ulong_t) pste->ste_minsiz / PAGESIZE) + 1));
#endif

    /*
     * pEnd = start of next pages data. Marks end of fixups for this page
     */

    pEnd = (uchar_t *)((ulong_t *)psfr)[ulPage + 1];
    psfr = (sfr_t *)((ulong_t *)psfr)[ulPage];

    /*
     * While fixup records exist for this page
     */

    while ((ulong_t)psfr < (ulong_t)pEnd)
    {
        ulSize = psfr->sfr_fixsize;
        pData =  (void *)&psfr->sfr_bytes;
        ulCount = psfr->sfr_soff;

        /*
         * pOffset points to chain offsets in fixup buffer
         */

        pOffset = (void *)((ulong_t)pData + ulSize);

        if (!psfr->sfr_fchained)
        {
            temp = pchPage + ulCount;
            ulCount = 0;                /* Force only one fixup */
            goto NotChainedLabel;
        }

        /*
         * Follow chained data
         */

        while (ulCount--)
        {

#ifdef LDRSTRICT
            ldrAssert(*pOffset < PAGESIZE);
#endif

            temp = &pchPage[*pOffset++];

NotChainedLabel:

            switch (ulSize)
            {
               case sizeof(uchar_t):
                   *(uchar_t *)temp = *(uchar_t *)pData;
                   break;
               case sizeof(ushort_t):
                   *(ushort_t *)temp = *(ushort_t *)pData;
                   break;
               case sizeof(ulong_t):
                   *(ulong_t *)temp = *(ulong_t *)pData;
                   break;
               default:
                   memcpy(temp, pData, ulSize);
            }
        }
        psfr = (void *)pOffset;
    }
    return NO_ERROR;
}


#pragma code_seg()
