#include <exec/types.h>
#include "defs.h"

static char rcsid [] = "$Id: fault.c,v 3.6 95/12/16 18:36:51 Martin_Apel Exp $";

PRIVATE int PagesWaiting = 0;

/*******************************************************************/

void AddFree (struct TrapStruct *ThisFault)

{
Forbid ();
AddTail (&Free, (struct Node*) ThisFault);
Permit ();
}

/*******************************************************************/

void AddPageReq (struct TrapStruct *ThisFault)

{
Forbid ();
AddTail (&PageReq, (struct Node*) ThisFault);
Permit ();
}

/*******************************************************************/

PRIVATE void InformTask (struct TrapStruct *ThisFault)

{
/* The page is back in memory. Signal the faulting task.
 */

PageFaultsInProgress--;
switch ((ULONG)ThisFault->FaultTask)
  {
  case REDUCE_TASK:  
       /* The pagehandler is trying to reduce the paging memory.
        * Check if it's enough, otherwise free another frame.
        */
       PRINT_DEB ("InformTask: Reduce", 0L);
       if ((NumPageFrames * PAGESIZE > CurrentConfig.MaxMem) && 
           (ThisFault->FaultAddress != NULL))
         {
         /* Do it again */
         ThisFault->FaultAddress = NULL;
         AddPageReq (ThisFault);
         SetSignal (1L << PageFaultSignal, 1L << PageFaultSignal);
         }
       else
         {
         PRINT_DEB ("Finished reducing memory usage", 0L);
         AddFree (ThisFault);
         LowMem--;
         }
       break;  

  /*********************************/
  case LOCK_TASK:
       PRINT_DEB ("InformTask: Lock", 0L);
       if (ThisFault->PageDescrAddr == NULL)
         {
         /* Just fetched the table. Do it again */
         AddPageReq (ThisFault);
         SetSignal (1L << PageFaultSignal, 1L << PageFaultSignal);
         }
       else
         {
         ThisPageLocked = (NumLocked + NumTables + 5 <= NumPageFrames) ||
                          AddFrame (0L);

         if (ThisPageLocked)
           {
           NumLocked++;
           Forbid ();
           *(ThisFault->PageDescrAddr) |= LOCKED;
           (*PFlushP) (ThisFault->FaultAddress);
           Permit ();
           }
         else
           PRINT_DEB ("Couldn't lock page. NumLocked = %ld", NumLocked);

         AddFree (ThisFault);
         Signal ((struct Task*)VM_ManagerProcess, 1L << LockAckSignal);
         }
       break;

  /*********************************/
  default:
/*     PRINT_DEB ("Signalling waiting task %lx", (ULONG)ThisFault->FaultTask); */
       CHECK_CONSISTENCY;
       Signal (ThisFault->FaultTask, 1L << ThisFault->WakeupSignal);
  }
}
/***********************************************************************/

void CheckWaitingFaults (void)

{
struct TrapStruct *ThisFault,
                  *Successor;

if (PagesWaiting == 0)
  return;

/* Because ThisFault might be removed at the end of the loop,
 * its successor is stored at the start of each looping
 */

PRINT_DEB ("Checking for waiting pages", 0L);

for (ThisFault = (struct TrapStruct*)InTransit.lh_Head,
     Successor = (struct TrapStruct*)ThisFault->TS_Node.mln_Succ;
     ThisFault->TS_Node.mln_Succ != NULL;
     ThisFault = Successor,
     Successor = (struct TrapStruct*)ThisFault->TS_Node.mln_Succ)
  {
  if (((ThisFault->PageDescrAddr == NULL) &&        /* Table has come in */
       (TABLE_RESIDENT_P (*(ThisFault->TableDescrAddr))))
                          ||
      ((ThisFault->PageDescrAddr != NULL) &&
        PAGE_RESIDENT_P (*(ThisFault->PageDescrAddr))))  /* Page has come in */
    {
    PRINT_DEB ("Signalling waiting task", 0L);
    Remove ((struct Node*)ThisFault);
    PagesWaiting--;
    InformTask (ThisFault);
    }
  }
}

/***********************************************************************/

PRIVATE void MakePageResident (struct TrapStruct *ThisFault)

{
struct FrameDescr *NewPage;

NewPage = ThisFault->FD;
NewPage->PageType = PT_PAGE;
NewPage->PageDescr = ThisFault->PageDescrAddr;
NewPage->LogAddr = PAGEADDR(ThisFault->FaultAddress);
NewPage->PageNumOnDisk = PGNUM_FROM_DESCR (*(ThisFault->PageDescrAddr));
NewPage->Modified = FALSE;

Forbid ();
*(ThisFault->PageDescrAddr) = ThisFault->PhysAddr | CACHE_MODE | PAGE_RESIDENT |
           ((ThisFault->FaultTask == (struct Task*)LOCK_TASK) ? LOCKED : 0);
(*PFlushP) (ThisFault->FaultAddress);
(*FlushVirt) (NewPage->LogAddr);
(*CPushP) (NewPage->PhysAddr);
Permit ();

#ifdef NEW_PRA
ReuseLate (ThisFault->FD);
#else
AddTail (&FrameList, (struct Node*)ThisFault->FD);
ThisFault->FD->InFrameList = TRUE;
#endif
}

/*****************************************************************/

PRIVATE void MakeTableResident (struct TrapStruct *ThisFault)

{
struct FrameDescr *NewTable;
ULONG *TableStart;
int i;
#ifdef DEBUG
ULONG *cur_descr;
#endif

PRINT_DEB ("MakeTableResident: for address %lx", ThisFault->FaultAddress);

NewTable = ThisFault->FD;
NewTable->PageType = PT_TABLE;
NewTable->LogTableStart = ALIGN_DOWN (ThisFault->FaultAddress,
                                      PAGESIZE * PAGESIZE / sizeof (ULONG));
NewTable->PageNumOnDisk = PGNUM_FROM_DESCR (*(ThisFault->TableDescrAddr));
PRINT_DEB ("MakeTableResident: PageNumOnDisk = %lx", NewTable->PageNumOnDisk);
NewTable->Modified = FALSE;

/* One Page of page descriptors contains 16 page tables. So the
 * pointers in the pointer table for those 16 page tables have to be
 * marked as valid
 */

#ifdef DEBUG
cur_descr = (ULONG*)ThisFault->PhysAddr;
for (i = 0; i < PAGESIZE / sizeof (ULONG); i++)
  {
  if (STATE_FROM_DESCR (*cur_descr++) == COMING_IN)
    {
    PRINT_DEB ("ERROR: Reading table with incoming pages", 0L);
    ColdReboot ();
    break;
    }
  }
#endif

TableStart = (ULONG*)ALIGN_DOWN (*(RootTable + ROOTINDEX (ThisFault->FaultAddress)),
                                 POINTERTABALIGN);
TableStart += ALIGN_DOWN (POINTERINDEX (ThisFault->FaultAddress),
                          PAGESIZE / sizeof (ULONG) / PAGES_PER_TABLE);

MarkPage ((ULONG)TableStart, NONCACHEABLE);

PRINT_DEB ("TableStart = %lx", (ULONG)TableStart);

Forbid ();
for (i = 0; i < PAGESIZE / sizeof (ULONG) / PAGES_PER_TABLE; i++)
  {
  *(TableStart + i) =
    (ThisFault->PhysAddr + i * PAGES_PER_TABLE * sizeof (ULONG)) | TABLE_RESIDENT;
  }

(*PFlushA)();
Permit ();
#ifdef NEW_PRA
ReuseLate (ThisFault->FD);
#else
AddTail (&FrameList, (struct Node*)ThisFault->FD);
ThisFault->FD->InFrameList = TRUE;
#endif
NumTables++;
}


/*****************************************************************/

PRIVATE void MakePageInvalid (struct FrameDescr *fd)

{
#ifdef DEBUG
if (!fd->InFrameList)
  {
  PRINT_DEB ("MakePageInvalid: Frame not in frame list", 0L);
  ColdReboot ();
  }
#endif

#ifdef NEW_PRA
RemoveFromAvail (fd);
#else
Remove ((struct Node*)fd);
fd->InFrameList = FALSE;
#endif

Forbid ();
*(fd->PageDescr) = BUILD_DESCR (fd->PageNumOnDisk, PAGED_OUT, PT_PAGE);
(*PFlushP) (fd->LogAddr);
(*CPushP) (fd->PhysAddr);
(*FlushVirt) (fd->LogAddr);
Permit ();
}

/*****************************************************************/

PRIVATE void MakeTableInvalid (struct FrameDescr *fd)

{
ULONG *TableStart;
int i;

PRINT_DEB ("MakeTableInvalid: PageNumOnDisk = %lx", fd->PageNumOnDisk);

#ifdef DEBUG
if (!fd->InFrameList)
  {
  PRINT_DEB ("MakeTableInvalid: Frame not in frame list", 0L);
  ColdReboot ();
  }
#endif

#ifdef NEW_PRA
RemoveFromAvail (fd);
#else
Remove ((struct Node*)fd);
fd->InFrameList = FALSE;
#endif

TableStart = (ULONG*)ALIGN_DOWN (*(RootTable + ROOTINDEX (fd->LogTableStart)),
                                 POINTERTABALIGN);
TableStart += POINTERINDEX (fd->LogTableStart);

Forbid ();
for (i = 0; i < PAGESIZE / sizeof (ULONG) / PAGES_PER_TABLE; i++)
  {
  *(TableStart + i) = BUILD_DESCR (fd->PageNumOnDisk, PAGED_OUT, PT_TABLE);
  }

(*PFlushA) ();
Permit ();
MarkPage ((ULONG)TableStart, CACHEABLE);
NumTables--;
}

/*****************************************************************/

PRIVATE void InstallNewTable (struct TrapStruct *ThisFault)

{
ULONG *TableStart;
int i;

PRINT_DEB ("InstallNewTable called", 0L);

/* Make all page descriptors in this page invalid */
TableStart = (ULONG*)ThisFault->PhysAddr;    /* Start of page table */
for (i = 0; i < PAGESIZE / sizeof (ULONG); i++)
  {
  *(TableStart + i) = BUILD_DESCR (LOCUNUSED, PAGED_OUT, PT_PAGE);
  }

MakeTableResident (ThisFault);
}

/*****************************************************************/

#ifndef NEW_PRA
PRIVATE BOOL TablePagingAllowed (struct FrameDescr *table_frame)

{
ULONG *cur_descr;
int i;

cur_descr = (ULONG*) table_frame->PhysAddr;
for (i = 0; i < PAGESIZE / sizeof (ULONG); i++, cur_descr++)
  {
  if (PAGE_RESIDENT_P (*cur_descr) || (STATE_FROM_DESCR (*cur_descr) == COMING_IN))
    return (FALSE);
  }
return (TRUE);
}
#endif

/*****************************************************************/

#define PAGE_NOT_IN_USE(fd) (((fd)->PageType == PT_PAGE) &&\
                             ((!PAGE_RESIDENT_P(*((fd)->PageDescr))) &&\
                              !MODIFIED_P(*fd)))

PRIVATE short Evict (struct TrapStruct *ThisFault)

{
/* A page fault has occurred. Find a page to put on disk.
 * Writes the physical address of the evicted page frame into
 * ThisFault->PhysAddr.
 * This routine returns after sending the write request to the handler
 * The chosen frame is removed from FrameList until it is used again
 */

struct FrameDescr *chosen,
                  *cur_frame;
BOOL found = FALSE;
static ULONG AvailableForFrames = 100000L;

/* Add another frame only when all of the following conditions apply:
 *   Dynamic policy
 *   Not currently evicting page-frames to make room for some physical request
 *   First frame is not empty
 *   First frame is not unused and unmodified (probably generated by a 
 *   FreeMem which marked this frame in this way.
 */
if ((NumPageFrames * PAGESIZE < CurrentConfig.MaxMem) && (!LowMem) &&
   (((struct FrameDescr*)(FrameList.lh_Head))->PageType != PT_EMPTY) &&
   !PAGE_NOT_IN_USE(((struct FrameDescr*)(FrameList.lh_Head))))

  {
  /* This way AddFrame will only retry allocation of a frame if some
   * other memory has been freed in the meantime (since the last
   * allocation failed).
   */
  if (AddFrame (AvailableForFrames))
    AvailableForFrames = 1L;
  else
    AvailableForFrames = DoOrigAvailMem (CurrentConfig.MemFlags);
  }

#ifdef NEW_PRA
chosen = FindRemoveablePage ();
#else

do
  {
  cur_frame = (struct FrameDescr*)RemHead (&FrameList);
#ifdef SCHED_STAT
  FramesExamined++;
#endif
#ifdef DEBUG
  if (cur_frame == NULL)
    {
    PRINT_DEB ("Internal error: FrameList empty", 0L);
    ColdReboot ();
    }
#endif
  AddTail (&FrameList, (struct Node*)cur_frame);

  if (cur_frame->PageType == PT_EMPTY)
    {
    ThisFault->PhysAddr = (ULONG)cur_frame->PhysAddr;
    ThisFault->FD = cur_frame;
    Remove ((struct Node*)cur_frame);
    cur_frame->InFrameList = FALSE;
    return (READY);
    }

  if ((cur_frame->PageType == PT_PAGE) && LOCKED_P (*(cur_frame->PageDescr)))
    ;
  else if ((cur_frame->PageType == PT_PAGE) &&
           (USED_P (*cur_frame->PageDescr)))
    {
    Forbid ();
    if (MODIFIED_P (*cur_frame))
      cur_frame->Modified = TRUE;
    *cur_frame->PageDescr &= ~(USED | MODIFIED);
    (*PFlushP) (cur_frame->LogAddr);
    Permit ();
    }
  else if (cur_frame->PageType == PT_TABLE)
    {
    /* Check if any of the descriptors in this page are still valid.
     * Otherwise this page table can be written to disk.
     */
    if (TablePagingAllowed (cur_frame))
      found = TRUE;
    }
  else
    found = TRUE;             /* Found an unused page */
  }
while (!found);

chosen = cur_frame;
#endif

/* Empty pages cannot occur here */
ThisFault->PhysAddr = chosen->PhysAddr;

ThisFault->FD = chosen;

#ifdef DEBUG

if (chosen->PageType == PT_PAGE)
  /* PRINT_DEB ("Evicting page frame for log. address %lx", chosen->LogAddr) */;
else if (chosen->PageType == PT_TABLE)
  PRINT_DEB ("Evicting table frame for address range %lx", chosen->LogTableStart);
else
  {
  PRINT_DEB ("Internal error: Empty page in Evict. Type %ld", (LONG)chosen->PageType);
  ColdReboot ();
  }
#endif
/* PRINT_DEB ("Physical address is %lx", ThisFault->PhysAddr); */

Forbid ();          /* Have to forbid here, otherwise this page could
                     * be modified after it has been tested for modification.
                     */
if ((chosen->PageType == PT_PAGE) && !MODIFIED_P (*chosen))
  {
  /* Don't need to write this one back.
   * Mark this address as invalid
   */
  MakePageInvalid (chosen);
  Permit ();
  return (READY);
  }
else
  {
  /* Write out modified page first */

  if (chosen->PageNumOnDisk != LOCUNUSED)
    FreePageOnDisk (chosen->PageNumOnDisk);
  chosen->PageNumOnDisk = AllocPageOnDisk ();

  if (chosen->PageType == PT_TABLE)
    MakeTableInvalid (chosen);
  else
    MakePageInvalid (chosen);

  Permit ();
  if (chosen->PageType == PT_TABLE)
    {
    PRINT_DEB ("PhysTableStart is %lx", (ULONG)chosen->PhysAddr);
    PRINT_DEB ("LogTableStart is %lx", chosen->LogTableStart);
    PRINT_DEB ("PageNumOnDisk = %lx", (ULONG)chosen->PageNumOnDisk);
    (*PFlushP) (ThisFault->FaultAddress);
    }
  return (WritePage (chosen->PageNumOnDisk, ThisFault));
  }
}

/**********************************************************************/

PRIVATE short GetNewPage (struct TrapStruct *ThisFault)

{
/* There's a place free at physical address ThisFault->PhysAddr.
 * Either read the needed page from disk or if this is the first reference
 * install a new one
 */

if (PGNUM_FROM_DESCR (*(ThisFault->PageDescrAddr)) == LOCUNUSED)
  {
  /* PRINT_DEB ("Installing new page", 0L); */
  MakePageResident (ThisFault);
  return (READY);
  }
else
  {
  if (ReadPage (PGNUM_FROM_DESCR (*(ThisFault->PageDescrAddr)), ThisFault) == READY)
    {
    MakePageResident (ThisFault);
    return (READY);
    }
  return (IN_PROGRESS);
  }
}

/**********************************************************************/

PRIVATE short GetNewTable (struct TrapStruct *ThisFault)

{
if (PGNUM_FROM_DESCR (*(ThisFault->TableDescrAddr)) == LOCUNUSED)
  {
  PRINT_DEB ("Installing new page table", 0L);
  InstallNewTable (ThisFault);
  return (READY);
  }
else
  {
  if (ReadPage (PGNUM_FROM_DESCR (*(ThisFault->TableDescrAddr)), ThisFault) == READY)
    {
    MakeTableResident (ThisFault);
    return (READY);
    }
  return (IN_PROGRESS);
  }
}

/***********************************************************************/

PRIVATE void HandleMissingPage (struct TrapStruct *ThisFault)

{
if (PAGE_RESIDENT_P (*(ThisFault->PageDescrAddr)))
  {
  /* Page was just requested by another task and has already come in */
  PRINT_DEB ("Faulted page has already come in", 0L);
  InformTask (ThisFault);
  }
else if (STATE_FROM_DESCR (*(ThisFault->PageDescrAddr)) == COMING_IN)
  {
  /* Page was just requested by another task and is in transit */
  AddTail (&InTransit, (struct Node*)ThisFault);
  PagesWaiting++;
  PRINT_DEB ("Putting fault to sleep", 0L);
  PRINT_DEB ("TableDescr is at %lx", (ULONG)ThisFault->TableDescrAddr);
  PRINT_DEB ("TableDescr contains %lx", *(ThisFault->TableDescrAddr));
  PRINT_DEB ("PageDescr is at %lx", (ULONG)ThisFault->PageDescrAddr);
  PRINT_DEB ("PageDescr contains %lx", *(ThisFault->PageDescrAddr));
  }
else
  {
  /* Start handling */
  *(ThisFault->PageDescrAddr) =
     BUILD_DESCR (PGNUM_FROM_DESCR (*(ThisFault->PageDescrAddr)),
                  COMING_IN, TYPE_FROM_DESCR (*(ThisFault->PageDescrAddr)));

  (*PFlushP) (ThisFault->FaultAddress);

  if (Evict (ThisFault) == READY)
    {
    if (GetNewPage (ThisFault) == READY)
      {
      InformTask (ThisFault);
      CheckWaitingFaults ();
      }
    }
  }
}

/***********************************************************************/

PRIVATE void HandleMissingTable (struct TrapStruct *ThisFault)

{
PRINT_DEB ("Table missing", 0L);

if (TABLE_RESIDENT_P (*(ThisFault->TableDescrAddr)))
  {
  /* Table was requested by another task and has already come in */
  PRINT_DEB ("Faulted table has already come in", 0L);
  InformTask (ThisFault);
  }
else if (STATE_FROM_DESCR (*(ThisFault->TableDescrAddr)) == COMING_IN)
  {
  PRINT_DEB ("Requested table is coming in", 0L);
  AddTail (&InTransit, (struct Node*)ThisFault);
  PagesWaiting++;
  }
else
  {
  /* Get this table */
  *(ThisFault->TableDescrAddr) =
     BUILD_DESCR (PGNUM_FROM_DESCR (*(ThisFault->TableDescrAddr)),
                  COMING_IN, TYPE_FROM_DESCR (*(ThisFault->TableDescrAddr)));

  (*PFlushA) ();

  if (Evict (ThisFault) == READY)
    {
    if (GetNewTable (ThisFault) == READY)
      {
      InformTask (ThisFault);
      CheckWaitingFaults ();
      }
    }
  }
}

/***********************************************************************/

PRIVATE struct FrameDescr *FindFrame (ULONG RequiredFlags)

{
/* Finds an evictable frame with the desired memory flags and returns it
 * In order to free the frames the reverse way they were allocated,
 * the following is done:
 * One frame to be evicted is chosen in the standard way.
 * A frame from the front of the FrameOrderList is chosen, which
 * is not currently involved in paging (PageDescr != INVALID).
 * This frame is copied to the evicted frame. The memory of the chosen
 * frame is freed then.
 */

struct FrameDescr *tmp_fd;
struct MinNode    *tmp_alloc_node;
BOOL found = FALSE;

tmp_alloc_node = (struct MinNode*)FrameAllocList.lh_Head;

while (!found)
  {
  tmp_fd = (struct FrameDescr*)(tmp_alloc_node - 1);
  if (tmp_fd->InFrameList)
    {
    switch (tmp_fd->PageType)
      {
      case PT_PAGE : if (!LOCKED_P (*(tmp_fd->PageDescr)))
                       found = TRUE;
                     break;
      case PT_TABLE: if (TablePagingAllowed (tmp_fd))
                       found = TRUE;
                     break;
      case PT_EMPTY: found = TRUE;
                     break;
      }
    }

  /* Makes use of the evaluation order of boolean expressions */
  if (!found || ((RequiredFlags & TypeOfMem ((APTR)tmp_fd->PhysAddr))
                 != RequiredFlags))
    {
    found = FALSE;
    tmp_alloc_node = tmp_alloc_node->mln_Succ;
    if (tmp_alloc_node->mln_Succ == 0)
      return (NULL);
    }
  }

return (tmp_fd);
}

/***********************************************************************/

PRIVATE short RemFrameFinish (struct TrapStruct *ThisFault)

{

struct FrameDescr *rem_frame;           /* The frame to be removed */
ULONG phys_addr;
BOOL modified;
ULONG DesiredFlags;

/* If more than 25 frames are requested, frames are not copied.
 * Apparently more frames are needed so this will only waste
 * time.
 */

if ((LONG)ThisFault->RemFrameSize - 
    (LONG)DoOrigAvailMem (ThisFault->RemFrameFlags | MEMF_PUBLIC) 
    > 25 * PAGESIZE)
  {
  PRINT_DEB ("Needing a lot of frames. Not copying", 0L);
  Remove ((struct Node*)&(ThisFault->FD->OrderNode));
  FreeMem ((APTR)ThisFault->FD->PhysAddr, PAGESIZE);
  FreeMem (ThisFault->FD, sizeof (struct FrameDescr));
  ThisFault->FramesRemoved++;
  ThisFault->RemFrameSize -= PAGESIZE;
  NumPageFrames--;

  /* Try again */
  Forbid ();
  AddHead (&PageReq, (struct Node*) ThisFault);
  Permit ();
  SetSignal (1L << PageFaultSignal, 1L << PageFaultSignal);
  PageFaultsInProgress--;
  PRINT_DEB ("Retrying RemFrame", 0L);

  PRINT_DEB ("Page frames left: %ld", NumPageFrames);
  return (IN_PROGRESS);
  }

DesiredFlags = ThisFault->RemFrameFlags &
               (MEMF_PUBLIC | MEMF_FAST | MEMF_CHIP);

/* If chip memory is not required explicitly, try to free a frame in
 * fast memory. This way program code is run much faster.
 */
if (!(DesiredFlags & MEMF_CHIP))
  {
  if ((rem_frame = FindFrame (DesiredFlags | MEMF_FAST)) == NULL)
    rem_frame = FindFrame (DesiredFlags);
  }
else
  rem_frame = FindFrame (DesiredFlags);

if (rem_frame == NULL)
  {
  PRINT_DEB ("Didn't find frame with desired flags %lx", DesiredFlags);
  ThisFault->FD->PageType = PT_EMPTY;
  ThisFault->FD->Modified = FALSE;
  ThisFault->FD->PageNumOnDisk = LOCUNUSED;
  AddHead (&FrameList, (struct Node*)ThisFault->FD);
  ThisFault->FD->InFrameList = TRUE;
  return (READY);
  }

phys_addr = rem_frame->PhysAddr;
Forbid ();
switch (rem_frame->PageType)
  {
  case PT_PAGE:
    (*FlushVirt) (phys_addr);        /* == logical address here */
    CopyMemQuick ((APTR)phys_addr,
                  (APTR)ThisFault->PhysAddr, PAGESIZE);
    modified = MODIFIED_P (*rem_frame);
    MakePageInvalid (rem_frame);
    ThisFault->FaultAddress = rem_frame->LogAddr;
    ThisFault->PageDescrAddr = rem_frame->PageDescr;
    MakePageResident (ThisFault);
    ThisFault->FD->Modified = modified;
    break;
  case PT_TABLE:
    PRINT_DEB ("RemFrameFinish: Removing table. Copying to %lx", ThisFault->PhysAddr);
    (*FlushVirt) (phys_addr);        /* == logical address here */
    CopyMemQuick ((APTR)phys_addr,
                  (APTR)ThisFault->PhysAddr, PAGESIZE);
    MakeTableInvalid (rem_frame);
    ThisFault->FaultAddress = rem_frame->LogTableStart;
    ThisFault->TableDescrAddr = (ULONG*)
               ALIGN_DOWN (*(RootTable + ROOTINDEX (ThisFault->FaultAddress)),
                           POINTERTABALIGN);
    ThisFault->TableDescrAddr += POINTERINDEX (ThisFault->FaultAddress);
    MakeTableResident (ThisFault);
    break;
  case PT_EMPTY:
    PRINT_DEB ("RemFrameFinish: Removing empty page", 0L);
    ThisFault->FD->PageType = PT_EMPTY;
    ThisFault->FD->Modified = FALSE;
    ThisFault->FD->PageNumOnDisk = LOCUNUSED;
#ifdef DEBUG
    if (!rem_frame->InFrameList)
      {
      PRINT_DEB ("RemFrameFinish: Removing frame not in frame list", 0L);
      ColdReboot ();
      }
#endif
    Remove ((struct Node*)rem_frame);
    rem_frame->InFrameList = FALSE;
    AddHead (&FrameList, (struct Node*)ThisFault->FD);
    ThisFault->FD->InFrameList = TRUE;
    break;
  }

Permit ();

Remove ((struct Node*)&(rem_frame->OrderNode));
FreeMem ((APTR)rem_frame->PhysAddr, PAGESIZE);
FreeMem (rem_frame, sizeof (struct FrameDescr));
ThisFault->FramesRemoved++;
NumPageFrames--;

PRINT_DEB ("Page frames left: %ld", NumPageFrames);
return (READY);
}

/***********************************************************************/

PRIVATE void RemFrame (struct TrapStruct *ThisFault)

{
PRINT_DEB ("RemFrame called with flags %lx", ThisFault->RemFrameFlags);
PRINT_DEB ("                     size  %lx", ThisFault->RemFrameSize);

if (((CurrentConfig.PageDev == PD_FILE) && 
     (ThisFault->FaultTask == (struct Task*)FileHandlerProcess)) ||
     (NumPageFrames * PAGESIZE <= CurrentConfig.MinMem) || 
     ((NumLocked + 3) >= NumPageFrames))
  {
  PRINT_DEB ("Not enough frames", 0L);
  InformTask (ThisFault);
  return;
  }

if (Evict (ThisFault) == READY)
  {
  if (RemFrameFinish (ThisFault) == READY)
    InformTask (ThisFault);
  }
}

/***********************************************************************/

void HandlePageFault (void)

{
struct TrapStruct *ThisFault;
ULONG *PageTable;

Forbid ();
ThisFault = (struct TrapStruct*)RemHead (&PageReq);
Permit ();

while (ThisFault != NULL)
  {
  /* PRINT_DEB ("FaultAddress is %08lx", ThisFault->FaultAddress); */
  PageFaultsInProgress++;
  CHECK_CONSISTENCY;

  if (ThisFault->FaultAddress == NULL)
    RemFrame (ThisFault);          /* Free a frame */
  else 
    {
    NumPageFaults++;

    ThisFault->TableDescrAddr = (ULONG*)
               ALIGN_DOWN (*(RootTable + ROOTINDEX (ThisFault->FaultAddress)),
                           POINTERTABALIGN);
    ThisFault->TableDescrAddr += POINTERINDEX (ThisFault->FaultAddress);

    if (TABLE_RESIDENT_P (*(ThisFault->TableDescrAddr)))
      {
      PageTable = (ULONG*) ALIGN_DOWN (*(ThisFault->TableDescrAddr), PAGETABALIGN);
      ThisFault->PageDescrAddr = PageTable + PAGEINDEX(ThisFault->FaultAddress);
      HandleMissingPage (ThisFault);
      }
    else
      {
      ThisFault->PageDescrAddr = NULL;
      HandleMissingTable (ThisFault);
      }
    }
  Forbid ();
  ThisFault = (struct TrapStruct*)RemHead (&PageReq);
  Permit ();
  }
}

/*****************************************************************/

void ReadReturned (struct TrapStruct *ThisFault)

{
/* PRINT_DEB ("Returned from reading", 0L); */

if (ThisFault->PageDescrAddr == NULL)
  MakeTableResident (ThisFault);
else
  MakePageResident (ThisFault);
InformTask (ThisFault);
}

/*****************************************************************/

void WriteReturned (struct TrapStruct *ThisFault)

{
short state;

/* PRINT_DEB ("Returned from writing", 0L); */
if (ThisFault->FaultAddress == NULL)
  state = RemFrameFinish (ThisFault);
else if (ThisFault->PageDescrAddr == NULL)
  state = GetNewTable (ThisFault);
else
  state = GetNewPage (ThisFault);

if (state == READY)
  InformTask (ThisFault);
}

/*****************************************************************/

BOOL AddFrame (ULONG priority)

/* This function allocates a pageframe and the accompanying frame
 * descriptor. If it is successfully allocated, it is put into
 * the FrameList. Otherwise everything will be freed and FALSE
 * will be returned.
 */

{
char *frame;
struct FrameDescr *FD;

/* PRINT_DEB ("AllocFrame called", 0L); */

if ((frame = AllocAligned (PAGESIZE, CurrentConfig.MemFlags | MEMF_REVERSE,
                           PAGEALIGN, priority)) == NULL)
  {
  /* PRINT_DEB ("Couldn't allocate mem for page-frame", 0L); */
  return (FALSE);
  }

if ((FD = DoOrigAllocMem (sizeof (struct FrameDescr), MEMF_PUBLIC)) == NULL)
  {
  PRINT_DEB ("No mem for frame descriptor", 0L);
  FreeMem (frame, PAGESIZE);
  return (FALSE);
  }

FD->PageType = PT_EMPTY;
FD->Modified = FALSE;
FD->PhysAddr = (ULONG)frame;

AddHead (&FrameList, (struct Node*)FD);
FD->InFrameList = TRUE;
AddHead (&FrameAllocList, (struct Node*)&(FD->OrderNode));

NumPageFrames++;
return (TRUE);
}

/***********************************************************************/

void RemAllFrames (void)

{
struct FrameDescr *tmp_fd;

while ((tmp_fd = (struct FrameDescr*)RemHead (&FrameList)) != NULL)
  {
  Remove ((struct Node*)&(tmp_fd->OrderNode));

  /* The pages themselves are separately freed unless they are not marked
   * as empty.
   */
  if (tmp_fd->PageType == PT_EMPTY)
    FreeMem ((APTR)tmp_fd->PhysAddr, PAGESIZE);
  FreeMem (tmp_fd, sizeof (struct FrameDescr));
  }

NumPageFrames = 0;
}
