#define SCCSID  "@(#)pgfault.c  6.16 92/03/24"

/*
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 * Change History (Change Team changes):
 *
 * Defect Date    Author         Flag   Abstract
 * =============================================================================
 * 51579  9-9-92  Carlos Romero  car01  Revamp memory hooks II
 *
 * 76699 12-15-93 Ron Cadima  Make Perfview compile option with flag PERFVIEW
 */

#define FNO_NUM  FNO_PGFAULT_C
#include <fnomem.h>

#define INCL_ERRORS
#include <bseerr.h>
#include <osconst.h>
#include <ostypes.h>
#include <osmac.h>
#include <mi.h>
#include <dossymc.h>
#include <sigdef.h>
#include <except.h>
#include <smexport.h>
#include <pgexport.h>
#include <tkexport.h>
#include <infoseg.h>
#include <vmexport.h>
#include <seldesc.h>
#include <selxport.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <screen.h>
#include <sem.h>
#include <tkptda.h>
#define INCL_TYPES
#include <basedef.h>
#include <mvdmexp.h>
#include <pglocal.h>
#include <vmowner.h>
#include <ldrxport.h>
#include <kmode.h>
#include <except32.h>
#include <string.h>
#include <mmph.h>

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

#include <protmode.h>
#include <vmkh.h>            // must be after tkexport.h for KSEM definition
#include <sched.h>


#ifdef PGSTRICT
ulong_t PGTestPage1=0;  // special code for page tuning data
ulong_t PGTestPage2=0;
#endif

/* BUGBUG - the following should be moved to global include files */
/*    also see vminfo.c and trap.asm */

int PGTrap2FindPage (void);
ulong_t pgwTrap2FindPage(struct pte_s *ppte, ulong_t laddr);
extern ulong_t VMTrap2FindArena( VMHOB hob, struct ptda_s  **ppptda);
extern ulong_t NMIPhysAddr;
extern ulong_t NMIPTDA;
#define TRAP2_KILL_SYSTEM        0
#define TRAP2_KILL_APP           1
#define TRAP2_SYSTEM_RECOVERED   2


extern char smInitialized;


/***EP  PGPageFault - handle a linear address page fault
 *
 *      PGPageFault verifies the linear address is valid, swaps in
 *      the appropriate page table if necessary, and restores the page
 *      contents.
 *
 *          - pin down page table
 *          - translate page number to pte pointer
 *          - if invalid fault and page is part of vdm, call vdm fault handler
 *          - take the page semaphore
 *          - if not-present fault, force page present
 *          - release the page semaphore
 *          - unpin the page table
 *
 *      ENTRY   page - faulting page number
 *              errcode - processor error code
 *      EXIT    0 on success, else error code
 *
 *    if (page is within VDM alias space)
 *      page = page mod # ptes per page table
 *
 *    pin page table
 *    if (we did not block)
 *      translate page # to pte pointer
 *      if (page semaphore is busy)
 *          unpin page table
 *          block until semaphore released
 *      else
 *          if (page is invalid)
 *              if (page is within a VDM)
 *                  call VDM page fault handler
 *              else
 *                  error = access denied
 *          else
 *              if (page is already present)
 *                  -- must be a write fault
 *                  if (page is swap-on-write)
 *                      clear swap-on-write bit in vp
 *                  else
 *                      error = access denied
 *              else
 *                  if (write fault and page is not swap-on-write)
 *                      error = access denied
 *                  else if (page is guard page)
 *                      call exception manager
 *                  else
 *                      if (page is UVIRT)
 *                          set pte present bit
 *                      else
 *                          if (vp is not attached to a pf)
 *                          --  if (vp cache flag is set)
 *                          --      if (frame is in discardable page cache)
 *                          --          attach pf to vp
 *                          --      clear vp cache flag
 *                          --  else
 *                                  set page semaphore
 *                                  call pgGetPF
 *                                  set pf busy flag
 *                                  call pgReloadPage
 *                                  clear pf busy flag
 *                                  release page semaphore
 *                          set pte frame field
 *                          set pte present bit
 *                          increment pf reference count
 *
 *                      if (write fault)
 *                          clear swap-on-write bit in vp
 *
 *          unpin page table
 *    return
 */

int
PGPageFault(faultaddr, errcode)
ulong_t faultaddr;
ulong_t errcode;
{
    register page_t page;
    register struct vp_s *pvp=0;
    register struct pte_s *ppte;
    struct pf_s *ppf;
    struct pgdata_s *ppd;
    struct ptda_s *pptda = pPTDACur;
    extern ulong_t CpuStepping;
    ulong_t aulExceptionInfo[2];
    int error;
    int pdi;
    int fTK_FF_BUF = FALSE;
    char TCBBuf[TCBIOCopySize];

    page = faultaddr >> PAGESHIFT;

#ifdef PGSTRICT
    if (page >= PGTestPage1 && page <= PGTestPage2)  {
       _asm { NOP }
    }
#endif

    /* For the FAT file system there are several things we need to before
     * processing a page fault. First, we need to save the state FF_BUF in
     * the TCBForce_Flg, and reset the current TCBForce_Flg to indicate that
     * we do not have a buffer. Secondly, we need to save all the FAT file
     * system TCB variables as early as possible. The old code will only save
     * the variables when doing I/O to the swap file. But if we need to go the
     * loader which will read from an EXE or DLL the FAT variables would not
     * get saved. Therefore, we need to save the variables at this time in the
     * Page Fault process. Thirdly, we need to zero out TCBCurrBuf so that
     * no uses it.
     *
     */

    if (pTCBCur->TCBForcedActions & TK_FF_BUF){
       fTK_FF_BUF = TRUE;
       pTCBCur->TCBForcedActions &= ~TK_FF_BUF;
    }
    SaveFatTCBInfo(SSToDS(&TCBBuf));

    pTCBCur->TCBCurBuf = 0;

#ifdef PGSTRICT
    /*
     *  Make sure we aren't inside a beginnoblock/endnoblock section.
     */
    if (cTKNoBlock != 0) {
#ifdef  PMDEBUG
        printf("page fault within TKBeginNoBlock/TKEndNoBlock section\n");
#endif
        error = ERROR_ACCESS_DENIED;
        goto Error;
    }

    /*
     *  The ager isn't allowed to take any faults
     */
    if (pgpAgerTCB == pTCBCur && pgpAgerTCB != 0) {
#ifdef  PMDEBUG
        printf("page fault in ager\n");
#endif
        error = ERROR_ACCESS_DENIED;
        goto Error;
    }

    /*
     *  We should never fault on a page table -- all paging should take
     *  place through pin/unpin.
     */
    if ((page >= ((ulong_t) pgpCRPageTables >> PAGESHIFT) &&
         page < (((ulong_t) pgpCRPageTables >> PAGESHIFT) +
                (TKCPTDAMAX * MAXCR_PDE))) ||                  // GSR
// GSR          (TKCPTDAMAX * MAXCRPDE))) ||
// BUGBUG - what about shared region
        (page >= ((ulong_t) pgpSysPageTables >> PAGESHIFT) &&
         page < (((ulong_t) pgpSysPageTables >> PAGESHIFT) + MAXSYSPDE))) {
#ifdef  PMDEBUG
        printf("page fault on a page table\n");
#endif
        error = ERROR_ACCESS_DENIED;
        goto Error;
    }

    /*
     *  Can't take a page fault while swapping except during initialization
     */
    if ((pTCBCur->TCBfSwapping & SM_TCB_SWAPPING) && smInitialized > 1) {
#ifdef  PMDEBUG
        printf("page fault during swapper operation\n");
#endif
        error = ERROR_ACCESS_DENIED;
        goto Error;
    }

#endif

#ifdef PERFVIEW
/***PV  Perfview - mem queue length of active page faults
 *                 mem count of page faults
 *                 thread count of page faults
 *                 thread time waiting for page faults
 *
 *      Memory hooks 1 and 2 as described in DCR 614.
 *      Thread hooks 5 and 6 as described in DCR 614.
 */

    PvwMeasurement(mem,PageFault,0);    /* Perfview page fault hook */
#endif

    pgcPageFaultsActive++;              /* Increment count *after* hook */

    MMPHPrePGPageFault();

    pgcPageFaults++;

//    PGFaultCnt++;     /* Page faults sincs last dispatch  DCR1203 */
    pTCBCur->TCBSchFlags |= SCH_PAGE_FAULT;



    /*
     * Clear everything except PER_WRITE and PER_USER, and compensate
     * for B1 errata.
     */
    errcode &= pgErrCdMask;

    /*
     *  If the address is within the VDM pde alias address range, convert
     *  the page and pptda to point to the page within the CR mapped by
     *  the alias.
     *  We can tell if this page table is an alias by looking for
     *  VDMALIASSIG in the page table's allocated page count.  If
     *  this IS a vdm, a pointer to the ptda that owns the alias can
     *  be found by shifting left by 16 the pt present count and adding
     *  on the resident count.
     */
    if (page >= MINSYSPAGE &&
        pgcPteAllocated[(page - MINSYSPAGE) >> (PAGEDIRSHIFT - PAGESHIFT)] ==
                    (ushort_t) VDMALIASSIG) {

        pptda = (struct ptda_s *)
                ((pgcPtePresent[(page - MINSYSPAGE) >>
                                (PAGEDIRSHIFT - PAGESHIFT)] << 16) +
                pgcPteResident[(page - MINSYSPAGE) >>
                               (PAGEDIRSHIFT - PAGESHIFT)]);
        page %= NPTEPPT;
    }

    /*
     * Get the pgdata_s pointer.  pgPageToPd also checks the address to see if
     * it's in a valid arena.
     */
    if ((error = pgPageToPd(page, pptda, SSToDS(&ppd))) == 0) {
        /*
         * The page is in either system space or user space, ie. it's not
         * "in-between".
         */
        pdi = pgPagePdToPDI(page, ppd);
        ppte = pgPagePdTopPte(page, ppd);

        /*
         * Pin the page table.  ERROR_BLOCKED will be returned if we're on
         * a B1 and we blocked to load the page table.  In this case, restart
         * the instruction.
         */
        pgPinPtFault(pdi, ppd, pgflPinFault, error);
        if (error == 0 && ppte->pt_long != 0) {
            /*
             * Page table is present and the page is valid.
             */
            if ((ppte->pt_long & (PT_PRESENT|PT_UVIRT)) == 0) {
                /*
                 * Page is not present and is not uvirt.
                 */
                pvp = pgVPITopVP(ppte->pt_frame);
                if (pvp->vp_busy == 0) {
                    /*
                     * Do a sanity check.
                     */
                    pgStrict(ppte, pvp);

                    /*
                     * Page's semaphore is not busy.  Now check for an
                     * access violation.  We cannot use the PER_PROTECT
                     * (access violation) bit of the processor error code,
                     * since the processor will raise a not-present exception
                     * before it raises an access violation exception.
                     */
                    if ((errcode & ppte->pt_long) == errcode) {
                        /*
                         * Access is valid.
                         */
                        for (;;) {
                            if (pvp->vp_pf) {
                                /*
                                 * vp is cross linked to a pf, ie. the page
                                 * is reclaimable.
                                 */
                                if (page >= 16 || CpuStepping >= CPU_386D1 ||
                                    page == 0 || page == (pvp->vp_frame & 0xF)){
                                    /*
                                     * The page is in the correct location.
                                     * Reclaim the frame.
                                     */
                                    ppf = pgFrameToPF(pvp->vp_frame);
                                    if (pvp->vp_pfidle) {
                                        /*
                                         * PF is on the idle list.  Remove it.
                                         */
// This header comment "must" be kept on column 1 so that//car01
// preprocessor can include subroutine stack frame in    //car01
// pgframe.inc                                           //car01

#ifdef PERFVIEW
/***PV - Perfview measurement hook                       //car01
 *                                                       //car01
 *      TRACE MINOR = <PVWEI_Ct_mem_Recovered_0>,        //car01
 *      TP    = @,                                       //car01
 *      TYPE    = (PVW),                                 //car01
 *      GROUP   = NOSTRICT                               //car01
 */                                                      //car01
                                        PvwCnt(mem,Recovered,0); //car01
#endif
                                        pvp->vp_pfidle = 0;
                                        pgDeletePF(ppf);  /* clears lock cnt */
// This header comment "must" be kept on column 1 so that//car01
// preprocessor can include subroutine stack frame in    //car01
// pgframe.inc                                           //car01

#ifdef PERFVIEW
/***PV - Perfview measurement hook                               //car01
 *                                                               //car01
 *      TRACE MINOR = <PVWEI_Ct_mem_Reassigned_1>,               //car01
 *      TP    = @,                                               //car01
 *      TYPE    = (PVW),                                         //car01
 *      GROUP   = NOSTRICT                                       //car01
 */                                                              //car01
 //car01                                This line decrements ctReassigned
 //car01                                which was generically incremented in
 //car01                                PGDeletePF (PGFRAME.C)
                                        PvwCnt(mem,Reassigned,1); //car01
#endif
                                        ppf->pf_refcount = 0;
                                    }

                                    ++ppf->pf_refcount;
                                    ppte->pt_frame = pvp->vp_frame;
                                    ppte->pt_present = 1;
                                    ppd->pd_pcpresent[pdi]++;

                                    if (ppte->pt_dirty == 0) {
                                        /*
                                         * Page is not a guard page.
                                         * We're done.
                                         */
                                      ErrorUnpin:
#ifdef PGVERYSTRICT
                                        /*
                                         * Do a sanity check again.
                                         */
                                        pgStrict(ppte, pvp);
#endif
                                        pgUnpinOnePt(pdi, ppd, 0, 0);
                                      Error:
                                        MMPHPostPGPageFault();

/***PV  Perfview - mem queue length of active page faults
 *                 thread time waiting for page faults
 *
 *      Memory hook 1 as described in DCR 614.
 *      Thread hook 6 as described in DCR 614.
 */

                                        --pgcPageFaultsActive;

#ifdef PERFVIEW
                                        PvwMeasurement(mem,PageFault,1);
#endif
                                        if (fTK_FF_BUF)
                                           pTCBCur->TCBForcedActions |= TK_FF_BUF;
                                        RestoreFatTCBInfo(SSToDS(&TCBBuf));
                                        return(error);
                                    } else {
                                        /*
                                         * Page is a guard page.  Let the
                                         * exception manager deal with it.
                                         * (uvirt guard pages are not allowed.)
                                         */
                                        pgUnpinOnePt(pdi, ppd, 0, 0);
                                        if (ppte->pt_uvirt == 0) {
                                            ppte->pt_dirty = 0;
                                            aulExceptionInfo[0] = (errcode & PER_WRITE) ? XCPT_WRITE_ACCESS : XCPT_READ_ACCESS;
                                            aulExceptionInfo[1] = page << PAGESHIFT;    // FaultAddr

                                            XCPTBuildR3DispatcherStack(EXCSW, // Trap #
                                                XCPT_GUARD_PAGE_VIOLATION,    // Xcpt #
                                                2,                            // Param Ct.
                                                SSToDS(aulExceptionInfo),     // Params
                                                FALSE,                        // fContinue
                                                NULL);                        // pERepRec

                                        }
                                        goto Error;
                                    }
                                } else {
                                    /*
                                     * Frame needs to be moved into the correct
                                     * location (dictated by 386 errata).
                                     */
                                    pgSetSem(pvp);
                                    pgAssert(pvp->vp_pfidle ||
                                        LOCKS(pgFrameToPF(pvp->vp_frame)) == 0);
                                    error = pgMoveFrame(ppte, page, pvp,
                                        PG_ERRATA, NULL);
                                    pgClearSemVP(pvp);
                                    if (error) {
                                        goto ErrorUnpin;
                                    }
                                }
                            } else {
                                /*
                                 * The frame is not reclaimable and must be
                                 * reloaded from disk.
                                 *
                                 * Take the semaphore and reload the page.
                                 */
                                pgSetSem(pvp);
                                error = pgReloadFrame(page, pvp, pPTDACur,
                                    page < 16 && page != 0 ? pg64Kerrata : 0,
                                    NULL);
                                pgClearSemVP(pvp);
                                if (error) {
                                    goto ErrorUnpin;
                                }
                            }
                        }
                    } else {
                        /*
                         * Access violation.
                         */
                        error = ERROR_INVALID_ACCESS;
                        goto ErrorUnpin;
                    }
                } else {
                    /*
                     * Page semaphore is busy.  Unpin the page table, wait
                     * for the semaphore to be released, and restart the
                     * instruction.  Since the most likely reason for blocking
                     * on the semaphore is because another thread is in the
                     * process of faulting the page in, it is faster to simply
                     * assume the page is present and restart the instruction
                     * than to go back up and repin the page table.
                     */
                    pvp->vp_wanted = 1;
                    pgUnpinOnePt(pdi, ppd, 0, 0);
                    error = TKSleep(pvp, -1, 0, 0);
                    goto Error;
                }
            } else {
                /*
                 * Page is present or is uvirt.  If it's present and we're on
                 * a B1 386, then we know we haven't blocked up to this point
                 * and therefore this must be an access violation (it's the
                 * only way we have of detecting access violations on a B1).
                 * Otherwise if it's present and this isn't a B1, then the
                 * processor error code is valid and we can use that to detect
                 * access violations.  (It's possible that this isn't an access
                 * violation--we may have blocked during the pin and another
                 * thread could have faulted the page in.)
                 *
                 * If the page is a not-present uvirt, just mark it present.
                 */
                if (ppte->pt_present) {
                    /*
                     * Do a sanity check.
                     */
                    pgStrict(ppte, 0);

                    if (CpuStepping < CPU_386D1 ||
                        (errcode & ppte->pt_long) != errcode) {
                        error = ERROR_INVALID_ACCESS;
                    }
                } else {                                /* page is uvirt */
                    ppte->pt_present = 1;
                    ppd->pd_pcpresent[pdi]++;
                }
                goto ErrorUnpin;
            }
        } else {
            /*
             * We either got an error from pgPinPt(), or the page is invalid.
             */
            if (error == 0) {
                /*
                 * The pin was successful, but the page is invalid.  Unpin the
                 * page table.
                 */
                error = ERROR_ACCESS_DENIED;
                pgUnpinOnePt(pdi, ppd, 0, 0);
            }

            /*
             * If we faulted on an unallocated page in a vdm let the vdm
             * manager handle it.
             */
            if (error == ERROR_ACCESS_DENIED &&
                page <= (VDMM_MAXV86ADDR >> PAGESHIFT) && ppd->pd_pvdmalias) {
                error = VDMMDispatchPageFault((void *)(faultaddr));
            } else if (error == ERROR_BLOCKED) {
                /*
                 * We blocked pinning the page table.  We must be on a B1,
                 * so we need to restart the instruction in order to be able
                 * to detect an access violation.
                 */
                pgAssert(CpuStepping < CPU_386D1);
                error = 0;
            }
            goto Error;
        }
    }
    goto Error;
}


/***LP  pgForcePresent - force a page present
 *
 *      pgForcePresent reclaims or reloads a page that is marked not-present
 *      in its pte.
 *
 *      ENTRY   ppte - pointer to page table entry
 *              page - page number of page to load
 *              pptda - pointer to owning context's ptda
 *              pvp - pointer to page's vp structure
 *              flags - PG_NOBLOCK - don't block
 *                      PG_NOTIFYBLOCK - return ERROR_BLOCKED if we blocked
 *                      PG_ERRATA - must handle < 64K errata workaround
 *              lobound - low frame boundary of exclusion region
 *              hibound - high frame boundary of exclusion region
 */

int
pgForcePresent(ppte, page, pptda, pvp, flags, lobound, hibound)
register struct pte_s *ppte;
page_t page;
struct ptda_s *pptda;
register struct vp_s *pvp;
int flags;
frame_t lobound;
frame_t hibound;
{
    struct pf_s *ppf;
    struct pgdata_s *ppd;
    int error = 0;
    int blocked = 0;

    pgAssert(ppte->pt_present == 0);

    if (ppte->pt_uvirt == 0) {
        if (pvp->vp_pf) {
            /*
             * The vp is cross linked to a pf.  The frame is reclaimable.
             */
            if ((pvp->vp_frame > lobound && pvp->vp_frame < hibound) ||
                (flags & PG_ERRATA) && page != (pvp->vp_frame & 0xF)) {

                /*
                 * The page must be moved.  Make sure it's possible.
                 */
                ppf = pgFrameToPF(pvp->vp_frame);
                if (pvp->vp_pfidle == 0) {
                    if (ppf->pf_llock) {
                        error = ERROR_LONGLOCK;
                        goto Error;
                    } else if (ppf->pf_slock) {
                        /*
                         * BUGBUG
                         */
                        error = ERROR_SHORTLOCK;
                        goto Error;
                    }
                }
                if (flags & PG_NOBLOCK) {
                    error = ERROR_NOBLOCK;
                    goto Error;
                }

                if (error = pgMoveFrame(ppte, page, pvp, flags, NULL)) {
                     goto Error;
                }

                blocked = ERROR_BLOCKED;
            }

        } else {
            if (flags & PG_NOBLOCK) {
                error = ERROR_NOBLOCK;
                goto Error;
            }

            /*
             * The frame is not reclaimable and must be
             * reloaded from disk.
             */
            if (error = pgReloadFrame(page, pvp, pptda, flags, NULL)) {
                goto Error;
            }
            blocked = ERROR_BLOCKED;
        }

        ppf = pgFrameToPF(pvp->vp_frame);
        if (pvp->vp_pfidle) {
            /*
             * PF is on the idle list.  Remove it.
             */
            pvp->vp_pfidle = 0;
            pgDeletePF(ppf);        /* clears lock counts */
            ppf->pf_refcount = 0;
        }

        ++ppf->pf_refcount;
        ppte->pt_frame = pvp->vp_frame;

        if (ppte->pt_dirty) {
            /*
             * Page is a guard page.
             */
            ppte->pt_dirty = 0;
            error = ERROR_GUARDPAGE;
        }
    }
    pgPageToPd(page, pptda, SSToDS(&ppd));
    ppte->pt_present = 1;
    ++ppd->pd_pcpresent[pgPagePdToPDI(page, ppd)];
  Error:
    return(error ? error : (flags & PG_NOTIFYBLOCK) ? blocked : 0);
}


/***LP  pgReloadFrame - reload a not-present page
 *
 *      pgReloadFrame loads a discarded, swapped or aod page.  The page
 *      is assumed to be not reclaimable.  A frame will be allocated if
 *      necessary.  The page's semaphore must be owned by the calling
 *      thread.
 *
 *          - allocate frame if necessary
 *          - determine page state
 *          - copy page if swappable/discarded and page is in cache
 *          - call loader or swap manager to load or swap in the page
 *
 *      ENTRY   page - linear page number
 *              pvp - pointer to virtual page structure
 *              pptda - pointer to ptda of context owning the page
 *              flags -
 *                  PG_ERRATA   frame must satisfy errata workaround
 *                  PG_RESIDENT frame must reside in resident region
 *                  PG_LONG     frame must reside in resident region
 *                  PG_1M       frame must reside below 1M
 *                  PG_16M      frame must reside below 16M
 *              ppf - pointer to pf to load page into or NULL if one
 *                  needs to be allocated
 *      EXIT    0 on success, else error code
 *
 */

int
pgReloadFrame(page, pvp, pptda, flags, ppfcaller)
page_t page;
register struct vp_s *pvp;
struct ptda_s *pptda;
int flags;
struct pf_s *ppfcaller;
{
    register struct pf_s *ppf = ppfcaller;
    struct pf_s *ppfT;
    frame_t frame;
    ldrlf_t lf;
    int error = 0;
#ifdef PMDEBUG
    int    notifyDebug = 0;
    USHORT debughMTE;
    ULONG  debugPage;
#endif

    MMPHPrePGReloadPF();

//  fix novell requester problem                                       // 46552
//  since TSDs are commited not allocated there is no way to set the   // 46552
//  below 16 meg flag, and they must reside below 16 meg for the       // 46552
//  loader to read data into the user's ring0 stack                    // 46552
                                                                       // 46552
    if (page >= pageTKTSDs && page < pageTKTSDsEnd) {                  // 46552
        flags |= PG_16M;    /* must reside below 16 meg physical */    // 46552
    }                                                                  // 46552
// end                                                                 // 46552

    /*
     * Allocate a frame if we don't already have one and set the busy flag.
     */
    if (ppf == NULL) {
        if (error = pgGetPF(page, flags, SSToDS(&ppfT))) {
            goto Error;
        }
        ppf = ppfT;
        ppf->pf_busy = 1;
    } else {
        /*
         * The caller supplied a free frame.  Remove it from the free list.
         */
        pgAssert(ppf->pf_free);
        pgDeletePF(ppf);
        ppf->pf_free = 0;
    }
    frame = BLINK(ppf);
    BLINK(ppf) = 0;

    if ((pvp->vp_flags & (VP_DISCARDABLE | VP_DF)) == 0) {
        /*
         * Page is swappable and does not have a disk frame.
         */
        if (pvp->vp_block == VPB_ZEROFILL) {
            /*
             * Page is aod-zero fill.
             */
            memset((void *) pgAliasTemp(frame, 0), 0, PAGESIZE);
        } else if (pvp->vp_block != VPB_AOD) {
            /*
             * Page must have a broken swap frame.
             */
            pgAssert(pvp->vp_block == VPB_DFBROKEN);
            error = ERROR_SWAPIN_FAILED;
        }
    } else if (pvp->vp_flags & VP_DISCARDABLE) {
        /*
         * Page is discardable.
         */
        if ((pvp->vp_flags & VP_RESIDENT) == 0) {
            /*
             * Page is loader discardable.  Call the loader.
             */
            error = PGAllocBuf(1, PG_DISCARDABLE, frame,
                              SSToDS(&lf.lf_pallocbuf),
                              SSToDS(&lf.lf_sallocbuf), page);
            if (error == 0) {
                error = VMGetLaddr(pvp->vp_hob, SSToDS(&lf.lf_objaddr));
                pgAssert(error == NO_ERROR);

                lf.lf_objaddr += pvp->vp_obpg << PAGESHIFT;
                lf.lf_faultaddr = page << PAGESHIFT;
                lf.lf_bno  = pvp->vp_block;
                lf.lf_hMTE = VMHandleMap(pvp->vp_hob)->ob_hobmte;

                error = LDRGetPage(SSToDS(&lf));
                PGFreeBuf(lf.lf_pallocbuf);
                if (error != NO_ERROR && error != ERROR_INCACHE &&
                    error != ERROR_INTERRUPT) {
                    /*
                     * Set the error identifier.
                     */
                    pTSDCur->TSDerridFault = ERROR_LDRERROR;
                }
                #ifdef PMDEBUG
                else
                {
                  notifyDebug = 1;
                  debughMTE   = lf.lf_hMTE;
                  debugPage   = lf.lf_faultaddr;
                }
                #endif
            }
            MemStatIncr(ms_demandload);
        } else {
            /*
             * Page is discardable zero-fill.
             */
            memset((void *) pgAliasTemp(frame, 0), 0, PAGESIZE);
        }
    } else {
        /*
         * Page is swappable.  Swap it in.
         */
        pgAssert(pvp->vp_discardable == 0);
        if (error = SMSwapIn(pvp->vp_block, frame)) {
            if (error != ERROR_INTERRUPT) {
                pvp->vp_df = 0;
                if (error == ERROR_SWAP_IO_PROBLEMS) {
                    /*
                     * We got the page back ok, but the disk frame is bad.
                     */
                    error = 0;
#ifdef PGSTRICT
                    ppf->pf_block = -3;
#endif
                } else {
                    pvp->vp_block = VPB_DFBROKEN;
                    if (error != ERROR_INTERRUPT) {
                        pTSDCur->TSDerridFault = ERROR_SWAPERROR;
                    }
                }
            }
        }
    }

    /*
     * Clear the busy flag only if we allocated the frame.
     */
    if (ppfcaller == NULL) {
        ppf->pf_busy = 0;
    }

    if (error == 0) {
        /*
         * Cross link the vp and pf.
         */
        pgAssert((pvp->vp_flags & (VP_PF|VP_PFIDLE)) == 0);
        ppf->pf_block = pvp->vp_block;
        ppf->pf_pvp = pvp;
        ppf->pf_refcount = 0;
        pvp->vp_frame = frame;
        pvp->vp_pf = 1;
        if ((pvp->vp_flags & VP_DISCARDABLE)  &&
            ((pvp->vp_flags & VP_SOW) == 0) )
            pgDiscardableInmem++;
    } else {
        /*
         * Free the pf we were to use.
         */
        pgFreePF(ppf, frame);
    }

  Error:
    if (error == ERROR_INCACHE) {
        error = 0;
    }
    MMPHPostPGReloadPF();

#ifdef PMDEBUG
    if (notifyDebug)
      DebugPageLoaded(debughMTE,debugPage);
#endif
    return(error);
}


/***LP  pgMapPte - Worker function to remap ptes to a new frame.
 *
 *      pgMapPte is called by VMScanMaps (indirectly by pgMoveFrame) to remap
 *      all ptes referencing a source frame to a destination frame.  This
 *      routine will not block (and must not block).
 *
 *      ENTRY   pgmp        - pointer to pgmappte_s structure
 *              page        - page number
 *              pptda       - pointer to ptda
 *      RETURN  none
 *
 */

void
pgMapPte(pgmp, page, pptda)
register struct pgmappte_s *pgmp;
page_t page;
struct ptda_s *pptda;
{
    register struct pte_s *ppte;
    struct pgdata_s *ppd;
    ulong_t pdi;
    int error;

    /*
     * Get the pgdata pointer and page directory index.
     */
    if (pgPageToPd(page, pptda, SSToDS(&ppd)) == 0) {
        pdi = pgPagePdToPDI(page, ppd);
        if ((int)ppd->pd_pcpresent[pdi] > 0) {
            /*
             * The page table is present.
             */
            ppte = pgPagePdTopPte(page, ppd);

            if ((ppte->pt_long & (PT_FRAME | PT_PRESENT)) ==
                ((pgmp->mp_framesrc << PT_FRAMESHIFT) | PT_PRESENT)) {
                /*
                 * The page is present and maps the old frame.  Point it
                 * to the new frame and increment the pf reference count.
                 */
                ppte->pt_frame = pgmp->mp_framedes;
                ++pgmp->mp_ppfdes->pf_refcount;

                /*
                 * If we are stealing a TSD, recompute the variables
                 * stored for the SS image.
                 * Note that we do not steal the current thread's TSD
                 * for now.
                 */
                if (page >= pageTKTSDs && page < pageTKTSDsEnd) {
#ifdef MISCSTRICT
                    pgAssert((page - 1) << PAGESHIFT != (ulong_t)pTSDCur);
                    PGKStackRemap(((struct tsd_s *)
                                ((page - 1) << PAGESHIFT))->TSDpTCB);
#else
                    pgAssert((page) << PAGESHIFT != (ulong_t)pTSDCur);
                    PGKStackRemap(((struct tsd_s *)
                                ((page) << PAGESHIFT))->TSDpTCB);
#endif
                }

                if (page < 16 && CpuStepping < CPU_386D1 && page != 0 &&
                    page != (pgmp->mp_framedes & 0xf)) {
                    /*
                     * The errata requires us to mark the pte not-present.
                     */
                    if ((error = pgIdle(ppte, pgmp->mp_ppfdes, pdi, ppd)) ==
                        ERROR_WRITE_FAULT) {
                        /*
                         * There was an illegal write through this pte.  Set
                         * up for a call to pgIllegalWrite when we're done
                         * moving.
                         */
                        pgmp->mp_kill = TRUE;
                        pgmp->mp_page = page;
                        pgmp->mp_pptda = pptda;
                    } else if (error != 0) {
                        panic("Cannot idle page");
                    }
                    pgAssert(pgmp->mp_ppfsrc->pf_refcount > 0);
                    --pgmp->mp_ppfsrc->pf_refcount;
                }
            }
        }
    } else {
        pgAssert(FALSE);        /* page must be valid */
    }
}


/***    PGMakePresent - force a range of pages present
 *
 *      BUGBUG - this routine should be re-written to work quickly on a
 *               single page, or if we need multiple page operation, this
 *               routine should be junked and replaced with PGVerifyPermissions
 *
 *      ENTRY:  page - target page to force present
 *              pptda - target context
 *              npages - should be 1
 *              flags - must be 0
 *      EXIT:   0 if success, else error code
 */
int
PGMakePresent(page,pptda,npages,flags)
page_t page;
struct ptda_s *pptda;
ulong_t npages;
ulong_t flags;
{
    pgAssert(npages == 1 && flags == 0);
    return(PGVerifyPermission(page, pptda, 1, PG_FORCEPRESENT));
}
/***EP  PGTrap2FindPage - high level worker to find the physical page
 *                        that caused a trap2
 *
 *      PGTrap2FindPage is called by the trap2 handler to find the physical
 *      address and to determine if the page causing the trap can be removed
 *      (and it's data lost) without killing the system.  The system remains
 *      up when a private page is found.  The app is saved if a duplicate copy
 *      of the page is on disk else the app is terminated.
 *
 *      ENTRY   NONE
 *
 *
 *      RETURN  ulong_t     - TRAP2_KILL_APP - kill the current process
 *                            TRAP2_KILL_SYSTEM - kill the system
 *                            TRAP2_SYSTEM_RECOVERED - do nothing system ok
 */
int PGTrap2FindPage()
{
   int i;                  /* loop increment */
   int rc;                 /* return code */
   struct pte_s ppte_save;  /* place to save pte */
   ulong_t physaddr;        /* physical addresss found tobe bad */
   struct pte_s *ppde;      /* pointer to pde */
   struct pte_s *ppte;      /* pointer to pte */
   struct ptda_s  *pptda;  /* Pointer to PTDA */
   page_t page_tr;         /* page number that trap occurred on */
   struct pte_s *ppte_tr;     /* pte that trap occurred on */
   struct pgdata_s *ppd_tr;   /* pde that trap occurred on */
   ulong_t pdi_tr;      /* pdi that trap occurred on */
   struct pf_s *ppf;   /* pointer to pf */
   struct vp_s *pvp;   /* pointer to vp */
   ulong_t   linaddr;  /* linear address */

   /*    Find a resident pde and pte to be used in searching for the
    *    the bad memory.  Save the pte value when found
    */
   ppde = pgpPageDir+(NPTEPPT-1);     // get last pte in pde  0x3ff
   for (i=MAXSYSPDE; i>=0 ; i--, ppde--) {
      if (ppde->pt_present) {     // must be present
         break;
      } /* endif */
   } /* endfor */
   i--;  // minus 1 for 0 based indexing
   linaddr = MINSYSLADDR + (i * _4MEG);
   ppte =  pgpSysPageTables+(i*NPTEPPT);

   /*
    *  save orginal value of pte
    *  call worker to determine the physical address
    *  save physical address of failing page
    *  Restore orginal value of pte
    */
   ppte_save = *ppte;
   physaddr = pgwTrap2FindPage(ppte, linaddr);
   NMIPhysAddr = physaddr;
   *ppte = ppte_save;

   /*
    *  if -1 is returned the trap2 could not be reproduced, at this point
    *    we will return with kill the system
    *  else a physical address was returned, in this case determine the
    *    linear address and call VM to determine which arena (private, shared
    *    or system) if it falls within the private arena test if the page has
    *    another copy on disk, if so we can save the process otherwise the app
    *    must die, for shared or system arena page kill the system
    */
   if (physaddr != -1) {
      ppf = pgFrameToPF((physaddr >> 12));

      // can't be free and someone must be referencing it
      pgAssert(ppf->pf_refcount > 0 && ppf->pf_free ==0);

      pvp = ppf->pf_pvp;    // get pointer to vp

//    printf("ppf=%lx pvp=%lx pf.cref=%x vp.flg=%x\n",ppf,pvp,ppf->pf_refcount,pvp->vp_flags);

      /*
       * if the pf is locked (short or long) or the ref count > 1
       *    or if the vp is resident, busy, wanted or dirty : kill the system
       */
      if ( (ppf->pf_llock > 0)  ||
           (ppf->pf_slock > 0)  ||
           (pvp->vp_refcount != 1)  ||
           (pvp->vp_flags &
              (VP_RESIDENT | VP_BUSY | VP_WANTED | VP_DIRTY))) {
         return(TRAP2_KILL_SYSTEM);
      }

      linaddr=VMTrap2FindArena((VMHOB)pvp->vp_hob,SSToDS(&pptda));

      if (linaddr != -1) {   // if private arena
         /*
          *  calculate page number of page taking trap2
          */
         page_tr = (linaddr >> PAGESHIFT)  + pvp->vp_obpg;

         /*
          *  calculate pointer to page directory
          */
         if (pgPageToPd(page_tr, pptda, SSToDS(&ppd_tr)) != 0) {
            pgAssert(0);   // kill the system
            return(TRAP2_KILL_SYSTEM);
         }
         /*
          *  calculate page directory index
          */
         pdi_tr = pgPagePdToPDI(page_tr, ppd_tr);
         pgAssert( ((int)ppd_tr->pd_pcpresent[pdi_tr] > 0));

         /*
          *  calculate pointer to pte of page taking trap2
          */
         ppte_tr = pgPagePdTopPte(page_tr, ppd_tr);

        /*
         * if Page is swappable and doesn't have a disk frame, or
         * page is dirty and is swap-on-write.
         * Kill the app
         * NOTE: this "if" statement is duplicated in PGBuildGrpIOList
         *       and if one is updated, the other one should be too.
         */
        if ((pvp->vp_flags & (VP_DF|VP_DISCARDABLE)) == 0 ||
            ((pvp->vp_flags & (VP_SOW|VP_DIRTY)) == (VP_SOW|VP_DIRTY))) {
            rc = TRAP2_KILL_APP;
            /*
             *  Set pointer to pptda, for killing the app
             *  Note: pptda does not always == pPTDACur
             */
             NMIPTDA = pptda;
         } else {
            /*
             *  a clean copy of the page is somewhere on disk
             */
            rc = TRAP2_SYSTEM_RECOVERED;
         }
         /*
          * Call pgidle to fix the pte and vpp; as a side effect
          *   pgIdle puts the pf on the idle list
          */
         if ((pgIdle(ppte_tr,ppf,pdi_tr,ppd_tr)) != NO_ERROR)  {
            pgAssert(0);
            return(TRAP2_KILL_SYSTEM);
         }

         /*
          * Break the cross link between the vp and pf
          */
         pvp->vp_flags &= ~(VP_PF|VP_PFIDLE);
         if ((pvp->vp_flags & VP_DISCARDABLE) &&
            ((pvp->vp_flags & VP_SOW) == 0) )
            pgDiscardableInmem--;
         pvp->vp_block = ppf->pf_block;

         /*
          * Delete the pf from the idle list
          */
         pgDeletePF(ppf);

         /*
          *  Mark pf as long term locked with a ref count of 1
          */
         ppf->pf_flags &= ~(PF_FREE|PF_BUSY);
         ppf->pf_llock = 1;                         // make it long locked
         ppf->pf_refcount = 1;                      // dummy ref count of 1
         ppf->pf_pvp = (struct vp_s *)(pgVPBasePg << PAGESHIFT);
                                                    // set pvp to vp[0]
#ifdef PGSTRICT
         ppf->pf_block = -1;
#endif
      } else {      // if system or shared page : kill the system
         rc = TRAP2_KILL_SYSTEM;
      }
   } else {     // if physaddr == -1  couldn't reproduce trap2 : kill system
      rc = TRAP2_KILL_SYSTEM;
   }
   return(rc);
}
