Exemple #1
0
public class SwapFile {

  public static OpenFile swapFile;
  private static String swapName;
  private static int PAGESIZE = Machine.processor().pageSize;
  public static List<Integer> freePages;
  public static List<Integer> allocatedPages;
  private static Lock swapLock;
  private static byte[] memory = Machine.processor().getMemory();

  public static void initialize(String filename) {
    swapFile = ThreadedKernel.fileSystem.open(filename, true);
    swapName = filename;
    freePages = new LinkedList<Integer>();
    allocatedPages = new LinkedList<Integer>();
    swapLock = new Lock();
  }

  public static void close() {
    swapFile.close();
    ThreadedKernel.fileSystem.remove(swapName);
  }

  public static int insertPage(int spn, int ppn) {
    swapLock.acquire();
    int numBits = swapFile.write(spn * PAGESIZE, memory, ppn * PAGESIZE, PAGESIZE);
    // assert that numBits == PAGESIZE
    allocatedPages.add(spn);
    swapLock.release();
    return spn;
  }

  /* we will try to allocate a free page from the freepages*/
  public static int insertPage(int ppn) {
    int numBits = 0;
    int spn = swapFile.length() / PAGESIZE;
    if (freePages.size() > 0) {
      spn = freePages.remove(0);
    }
    return insertPage(spn, ppn);
  }

  public static void readPage(int spn, int ppn) {
    if (allocatedPages.contains(spn)) {
      swapLock.acquire();
      swapFile.read(spn * PAGESIZE, memory, ppn * PAGESIZE, PAGESIZE);
      swapLock.release();
    } else {
    }
  }

  public static void free(int page) {
    swapLock.acquire();
    freePages.add(page);
    swapLock.release();
  }
}
    /**
     * Initialize this kernel. Creates a synchronized console and sets the
     * processor's exception handler.
     */
    public void initialize(String[] args) {
	super.initialize(args);

	console = new SynchConsole(Machine.console());
	
	Machine.processor().setExceptionHandler(new Runnable() {
		public void run() { exceptionHandler(); }
	    });
    }
Exemple #3
0
  private void syncTLB() {
    for (int i = 0; i < Machine.processor().getTLBSize(); i++) {

      TranslationEntry tE = Machine.processor().readTLBEntry(i);
      if (tE.valid) {
        pageTable[tE.vpn].dirty = tE.dirty;
        pageTable[tE.vpn].used = tE.used;
      }
    }
  }
Exemple #4
0
  /**
   * Save the state of this process in preparation for a context switch. Called by
   * <tt>UThread.saveState()</tt>.
   */
  public void saveState() {
    for (int i = 0; i < Machine.processor().getTLBSize(); i++) {

      TranslationEntry tE = Machine.processor().readTLBEntry(i);
      if (tE.valid) {
        pageTable[tE.vpn].dirty = tE.dirty;
        pageTable[tE.vpn].used = tE.used;
      }
      tE.valid = false;
      Machine.processor().writeTLBEntry(i, tE);
      // savedState[i] = tE.vpn;
    }
  }
    /**
     * The exception handler. This handler is called by the processor whenever
     * a user instruction causes a processor exception.
     *
     * <p>
     * When the exception handler is invoked, interrupts are enabled, and the
     * processor's cause register contains an integer identifying the cause of
     * the exception (see the <tt>exceptionZZZ</tt> constants in the
     * <tt>Processor</tt> class). If the exception involves a bad virtual
     * address (e.g. page fault, TLB miss, read-only, bus error, or address
     * error), the processor's BadVAddr register identifies the virtual address
     * that caused the exception.
     */
    public void exceptionHandler() {
	Lib.assert(KThread.currentThread() instanceof UThread);

	UserProcess process = ((UThread) KThread.currentThread()).process;
	int cause = Machine.processor().readRegister(Processor.regCause);
	process.handleException(cause);
    }
Exemple #6
0
  /**
   * Transfer data from the specified array to this process's virtual memory. This method handles
   * address translation details. This method must <i>not</i> destroy the current process if an
   * error occurs, but instead should return the number of bytes successfully copied (or zero if no
   * data could be copied).
   *
   * @param vaddr the first byte of virtual memory to write.
   * @param data the array containing the data to transfer.
   * @param offset the first byte to transfer from the array.
   * @param length the number of bytes to transfer from the array to virtual memory.
   * @return the number of bytes successfully transferred.
   */
  public int writeVirtualMemory(int vaddr, byte[] data, int offset, int length) {
    Lib.assertTrue(offset >= 0 && length >= 0 && offset + length <= data.length);

    byte[] memory = Machine.processor().getMemory();

    int transferred = 0;
    while (length > 0 && offset < data.length) {
      int addrOffset = vaddr % 1024;
      int virtualPage = vaddr / 1024;

      if (virtualPage >= pageTable.length || virtualPage < 0) {
        break;
      }

      TranslationEntry pte = pageTable[virtualPage];
      if (!pte.valid || pte.readOnly) {
        break;
      }
      pte.used = true;
      pte.dirty = true;

      int physPage = pte.ppn;
      int physAddr = physPage * 1024 + addrOffset;

      int transferLength = Math.min(data.length - offset, Math.min(length, 1024 - addrOffset));
      System.arraycopy(data, offset, memory, physAddr, transferLength);
      vaddr += transferLength;
      offset += transferLength;
      length -= transferLength;
      transferred += transferLength;
    }

    return transferred;
  }
Exemple #7
0
  /** Handle the halt() system call. */
  private int handleHalt() {
    if (this.process_id != ROOT) {
      return 0;
    }
    Machine.halt();

    Lib.assertNotReached("Machine.halt() did not halt machine!");
    return 0;
  }
    /**
     * Start running user programs, by creating a process and running a shell
     * program in it. The name of the shell program it must run is returned by
     * <tt>Machine.getShellProgramName()</tt>.
     *
     * @see	nachos.machine.Machine#getShellProgramName
     */
    public void run() {
	super.run();

	UserProcess process = UserProcess.newUserProcess();
	
	String shellProgram = Machine.getShellProgramName();	
	Lib.assert(process.execute(shellProgram, new String[] { }));

	KThread.currentThread().finish();
    }
Exemple #9
0
  /**
   * Handle a user exception. Called by <tt>UserKernel.exceptionHandler()</tt> . The <i>cause</i>
   * argument identifies which exception occurred; see the <tt>Processor.exceptionZZZ</tt>
   * constants.
   *
   * @param cause the user exception that occurred.
   */
  public void handleException(int cause) {
    Processor processor = Machine.processor();

    switch (cause) {
      case Processor.exceptionTLBMiss:
        handleTLBMiss();
        break;
      default:
        super.handleException(cause);
        break;
    }
  }
Exemple #10
0
  /**
   * Initialize the processor's registers in preparation for running the program loaded into this
   * process. Set the PC register to point at the start function, set the stack pointer register to
   * point at the top of the stack, set the A0 and A1 registers to argc and argv, respectively, and
   * initialize all other registers to 0.
   */
  public void initRegisters() {
    Processor processor = Machine.processor();

    // by default, everything's 0
    for (int i = 0; i < processor.numUserRegisters; i++) processor.writeRegister(i, 0);

    // initialize PC and SP according
    processor.writeRegister(Processor.regPC, initialPC);
    processor.writeRegister(Processor.regSP, initialSP);

    // initialize the first two argument registers to argc and argv
    processor.writeRegister(Processor.regA0, argc);
    processor.writeRegister(Processor.regA1, argv);
  }
Exemple #11
0
  /**
   * Allocates memory for this process, and loads the COFF sections into memory. If this returns
   * successfully, the process will definitely be run (this is the last step in process
   * initialization that can fail).
   *
   * @return <tt>true</tt> if the sections were successfully loaded.
   */
  protected boolean loadSections() {
    if (numPages > Machine.processor().getNumPhysPages()) {
      coff.close();
      Lib.debug(dbgProcess, "\tinsufficient physical memory");
      return false;
    }
    pageTable = new TranslationEntry[numPages];

    for (int i = 0; i < numPages; i++) {
      int physPage = UserKernel.allocatePage();
      if (physPage < 0) {
        Lib.debug(dbgProcess, "\tunable to allocate pages; tried " + numPages + ", did " + i);
        for (int j = 0; j < i; j++) {
          if (pageTable[j].valid) {
            UserKernel.deallocatePage(pageTable[j].ppn);
            pageTable[j].valid = false;
          }
        }
        coff.close();
        return false;
      }
      pageTable[i] = new TranslationEntry(i, physPage, true, false, false, false);
    }

    // load sections
    for (int s = 0; s < coff.getNumSections(); s++) {
      CoffSection section = coff.getSection(s);

      Lib.debug(
          dbgProcess,
          "\tinitializing " + section.getName() + " section (" + section.getLength() + " pages)");

      for (int i = 0; i < section.getLength(); i++) {
        int vpn = section.getFirstVPN() + i;

        // for now, just assume virtual addresses=physical addresses
        int ppn = pageTable[vpn].ppn;
        section.loadPage(i, ppn);
        if (section.isReadOnly()) {
          pageTable[vpn].readOnly = true;
        }
      }
    }

    coff.close();
    return true;
  }
Exemple #12
0
  /** Allocate a new process. */
  public UserProcess() {
    map = new HashMap<Integer, childProcess>();

    int numPhysPages = Machine.processor().getNumPhysPages();
    pageTable = new TranslationEntry[numPhysPages];
    for (int i = 0; i < numPhysPages; i++)
      pageTable[i] = new TranslationEntry(i, i, true, false, false, false);

    this.process_id = unique;
    unique++;

    openfiles = new HashMap<Integer, OpenFile>();
    available_descriptors =
        new ArrayList<Integer>(Arrays.asList(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
    openfiles.put(0, UserKernel.console.openForReading());
    openfiles.put(1, UserKernel.console.openForWriting());
  }
  /**
   * Start this autograder. Extract the <tt>-#</tt> arguments, call <tt>init()</tt>, load and
   * initialize the kernel, and call <tt>run()</tt>.
   *
   * @param privilege encapsulates privileged access to the Nachos machine.
   */
  public void start(Privilege privilege) {
    Lib.assertTrue(this.privilege == null, "start() called multiple times");
    this.privilege = privilege;

    String[] args = Machine.getCommandLineArguments();

    extractArguments(args);

    System.out.print(" grader");

    init();

    System.out.print("\n");

    kernel = (Kernel) Lib.constructObject(Config.getString("Kernel.kernel"));
    kernel.initialize(args);

    run();
  }
Exemple #14
0
  /**
   * Handle a user exception. Called by <tt>UserKernel.exceptionHandler()</tt>. The <i>cause</i>
   * argument identifies which exception occurred; see the <tt>Processor.exceptionZZZ</tt>
   * constants.
   *
   * @param cause the user exception that occurred.
   */
  public void handleException(int cause) {
    Processor processor = Machine.processor();

    switch (cause) {
      case Processor.exceptionSyscall:
        int result =
            handleSyscall(
                processor.readRegister(Processor.regV0),
                processor.readRegister(Processor.regA0),
                processor.readRegister(Processor.regA1),
                processor.readRegister(Processor.regA2),
                processor.readRegister(Processor.regA3));
        processor.writeRegister(Processor.regV0, result);
        processor.advancePC();
        break;

      default:
        Lib.debug(dbgProcess, "Unexpected exception: " + Processor.exceptionNames[cause]);
        handleExit(-1);
        Lib.assertNotReached("Unexpected exception");
    }
  }
Exemple #15
0
 /**
  * Allocate and return a new process of the correct class. The class name is specified by the
  * <tt>nachos.conf</tt> key <tt>Kernel.processClassName</tt>.
  *
  * @return a new process of the correct class.
  */
 public static UserProcess newUserProcess() {
   return (UserProcess) Lib.constructObject(Machine.getProcessClassName());
 }
 private void delay() {
   long time = Machine.timer().getTime();
   int amount = 1000;
   ThreadedKernel.alarm.waitUntil(amount);
   Lib.my_assert(Machine.timer().getTime() >= time + amount);
 }
Exemple #17
0
  private void allocatePage(int vpn) {
    // System.out.println("TLBMiss: VPN:" + vpn + " of Process " + processID);
    TranslationEntry t = pageTable[vpn];
    if (!t.valid) {
      // System.out.println("Allocating VPN: " + t.vpn);
      lock.acquire();
      if (UserKernel.freePages.size() > 0) {
        int ppn = ((Integer) UserKernel.freePages.removeFirst()).intValue();
        t.valid = true;
        if (!t.dirty) {
          t.ppn = ppn;
          IPT[ppn] = new IPTEntry();
          IPT[ppn].process = this;
          IPT[ppn].tE = t;

          boolean isCoffSection = false;
          for (int s = 0; s < coff.getNumSections(); s++) {
            if (isCoffSection) {
              break;
            }
            CoffSection section = coff.getSection(s);
            if (section.getFirstVPN() > t.vpn) {
              continue;
            }
            for (int i = 0; i < section.getLength(); i++) {
              int svpn = section.getFirstVPN() + i;
              if (svpn == t.vpn) {
                section.loadPage(i, pinVirtualPage(t.vpn, false));
                //                System.out.println("VPN \"" + t.vpn + "\" is a COFF section");
                // System.out.println("COFF section loaded into VPN \"" + t.vpn + "\" PPN \"" +
                // t.ppn + "\"");
                isCoffSection = true;
                break;
              }
            }
          }

          if (!isCoffSection) {
            //            System.out.println("VPN \"" + t.vpn + "\" is NOT a COFF section");
            // System.out.println("PPN \"" + t.ppn + "\" Zero'd Out");
            byte[] data = new byte[Processor.pageSize];
            // writeVirtualMemory(t.vpn, data, 0, Processor.pageSize);
            byte[] memory = Machine.processor().getMemory();
            System.arraycopy(data, 0, memory, t.ppn * Processor.pageSize, Processor.pageSize);
          }
        } else // Dirty
        {
          //          System.out.println("VPN \"" + t.vpn + "\" has been swapped out, swaping into
          // PPN \"" + ppn + "\"");
          int spn = t.ppn;
          byte[] data = new byte[pageSize];
          byte[] memory = Machine.processor().getMemory();
          OpenFile swap = ThreadedKernel.fileSystem.open(".Nachos.swp", false);
          swap.read(spn * pageSize, memory, ppn * pageSize, pageSize);
          swap.close();
          t.ppn = ppn;
          // System.out.println(spn);
          freeSwapPages.set(spn, true);
          IPT[ppn] = new IPTEntry();
          IPT[ppn].process = this;
          IPT[ppn].tE = t;
          //          System.out.println("SPN \"" + spn + "\" has been swapped in.");
        }
      } else {
        syncTLB();

        // Clock Algorithm
        unpinnedPageLock.acquire();
        int numPinnedPages = 0;
        while (IPT[victim] == null || IPT[victim].tE.used || IPT[victim].pinCount) {
          if (IPT[victim] != null) {
            IPT[victim].tE.used = false;
            // System.out.println("PPN " + victim + " is used.");
          }
          if (IPT[victim].pinCount) {
            numPinnedPages++;
            // System.out.println("PPN " + victim + " is pinned.");
          }
          if (numPinnedPages == IPT.length) unpinnedPage.sleep();
          //          System.out.println("This Process is " + this.processID + " and the IPT's
          // process is " + IPT[victim].processID);
          victim = (victim + 1) % Machine.processor().getNumPhysPages();
        }
        // System.out.println("PPN " + victim + " will be evicted.");
        unpinnedPageLock.release();
        int evict = IPT[victim].tE.vpn;
        VMProcess evictedOwner = IPT[victim].process;
        int evictedPPN = victim;
        victim = (victim + 1) % Machine.processor().getNumPhysPages();

        // System.out.println("Not enough pysical memory, evicting VPN \"" + evict + "\" of process
        // " + evictedOwner.processID);
        // Swap Files
        if (evictedOwner.isDirty(evict)) {
          // System.out.println("VPN \"" + evict + "\" is dirty, swapping out.");
          OpenFile swap = ThreadedKernel.fileSystem.open(".Nachos.swp", false);
          int spn = 0;
          for (spn = 0; spn < freeSwapPages.size(); spn++) {
            if (freeSwapPages.get(spn)) {
              break;
            }
          }
          if (spn == freeSwapPages.size()) {
            freeSwapPages.add(false);
          } else {
            freeSwapPages.set(spn, false);
          }
          UserKernel.freePages.add(evictedPPN);
          byte[] memory = Machine.processor().getMemory();
          swap.write(spn * pageSize, memory, evictedPPN * pageSize, pageSize);
          swap.close();
          if (evictedOwner.processID != this.processID) {
            evictedOwner.evict(evict, spn);
          } else {
            pageTable[evict].ppn = spn;
            pageTable[evict].valid = false;
          }
          for (int i = 0; i < Machine.processor().getTLBSize(); i++) {
            TranslationEntry tE = Machine.processor().readTLBEntry(i);
            if (tE.vpn == evict && tE.ppn == evictedPPN) {
              //              System.out.println("TLBEntry " + i + " invalid");
              tE.valid = false;
              Machine.processor().writeTLBEntry(i, tE);
            }
          }
          lock.release();

          //          System.out.println("VPN \"" + evict + "\" of Process " + this.processID + "
          // swapped out to SPN \"" + spn + "\".");
          // Machine.processor().readTLBEntry(evict).valid = false;
          allocatePage(vpn);
          return;
        } else {
          if (evictedOwner.processID != this.processID) {
            // System.out.println("Evicted VPN " + evict + " of Process " + this.processID);
            evictedOwner.evict(evict);
          } else {
            pageTable[evict].valid = false;
          }
          UserKernel.freePages.add(evictedPPN);
          for (int i = 0; i < Machine.processor().getTLBSize(); i++) {
            TranslationEntry tE = Machine.processor().readTLBEntry(i);
            if (tE.vpn == evict && tE.ppn == evictedPPN) {
              //              System.out.println("TLBEntry " + i + " invalid");
              tE.valid = false;
              Machine.processor().writeTLBEntry(i, tE);
            }
          }
          lock.release();
          allocatePage(vpn);
          return;
        }
      }
      lock.release();
    }
    for (int i = 0; i < Machine.processor().getTLBSize(); i++) {
      TranslationEntry tE = Machine.processor().readTLBEntry(i);
      if (!tE.valid) {
        // System.out.println("Evicted TLBEntry " + i);
        Machine.processor().writeTLBEntry(i, t);
        // System.out.println(t.valid);
        return;
      }
    }
    syncTLB();
    int evictedEntry = (int) (Math.random() * Machine.processor().getTLBSize());
    TranslationEntry tE = Machine.processor().readTLBEntry(evictedEntry);
    pageTable[tE.vpn] = tE;
    Machine.processor().writeTLBEntry(evictedEntry, t);
    // System.out.println("Evicted TLBEntry " + evictedEntry + " randomly");
    // System.out.println("TE entered is VPN \"" + t.vpn + "\" PPN \"" + t.ppn + "\"");

  }
Exemple #18
0
 public void handleTLBMiss() {
   int vaddr = Machine.processor().readRegister(Processor.regBadVAddr);
   int vpn = Processor.pageFromAddress(vaddr);
   allocatePage(vpn);
 }
Exemple #19
0
 /**
  * Restore the state of this process after a context switch. Called by
  * <tt>UThread.restoreState()</tt>.
  */
 public void restoreState() {
   Machine.processor().setPageTable(pageTable);
 }
Exemple #20
0
/** A <tt>UserProcess</tt> that supports demand-paging. */
public class VMProcess extends UserProcess {
  /** Allocate a new process. */
  public VMProcess() {
    super();
  }

  /**
   * Save the state of this process in preparation for a context switch. Called by
   * <tt>UThread.saveState()</tt>.
   */
  public void saveState() {
    for (int i = 0; i < Machine.processor().getTLBSize(); i++) {

      TranslationEntry tE = Machine.processor().readTLBEntry(i);
      if (tE.valid) {
        pageTable[tE.vpn].dirty = tE.dirty;
        pageTable[tE.vpn].used = tE.used;
      }
      tE.valid = false;
      Machine.processor().writeTLBEntry(i, tE);
      // savedState[i] = tE.vpn;
    }
  }

  /**
   * Restore the state of this process after a context switch. Called by
   * <tt>UThread.restoreState()</tt>.
   */
  public void restoreState() {
    /*for(int i = 0; i < Machine.processor().getTLBSize(); i++)
    {

      TranslationEntry tE = Machine.processor().readTLBEntry(i);
      tE.valid = false;
      Machine.processor().writeTLBEntry(i, tE);
    }
    for(int i = 0; i < Machine.processor().getTLBSize(); i++)
    {
      if(pageTable[savedState[i]].valid)
      {
        Machine.processor().writeTLBEntry(i, pageTable[savedState[i]]);
      }
    }*/
  }

  /**
   * Initializes page tables for this process so that the executable can be demand-paged.
   *
   * @return <tt>true</tt> if successful.
   */
  protected boolean loadSections() {
    UserKernel.memoryLock.acquire();
    pageTable = new TranslationEntry[numPages];

    for (int vpn = 0; vpn < numPages; vpn++) {
      pageTable[vpn] = new TranslationEntry(vpn, -1, false, false, false, false);
    }

    UserKernel.memoryLock.release();

    // load sections

    for (int s = 0; s < coff.getNumSections(); s++) {
      CoffSection section = coff.getSection(s);

      Lib.debug(
          dbgProcess,
          "\tinitializing " + section.getName() + " section (" + section.getLength() + " pages)");

      for (int i = 0; i < section.getLength(); i++) {
        int vpn = section.getFirstVPN() + i;

        pageTable[vpn].readOnly = section.isReadOnly();
        // section.loadPage(i, pinVirtualPage(vpn, false));
      }
    }

    return true;
  }

  /** Release any resources allocated by <tt>loadSections()</tt>. */
  protected void unloadSections() {
    for (int vpn = 0; vpn < pageTable.length; vpn++) {
      if (pageTable[vpn].valid) {
        UserKernel.freePages.add(new Integer(pageTable[vpn].ppn));
        // IPT[pageTable[vpn].ppn] = null;
      }
    }
  }

  /**
   * Handle a user exception. Called by <tt>UserKernel.exceptionHandler()</tt> . The <i>cause</i>
   * argument identifies which exception occurred; see the <tt>Processor.exceptionZZZ</tt>
   * constants.
   *
   * @param cause the user exception that occurred.
   */
  public void handleException(int cause) {
    Processor processor = Machine.processor();

    switch (cause) {
      case Processor.exceptionTLBMiss:
        handleTLBMiss();
        break;
      default:
        super.handleException(cause);
        break;
    }
  }

  protected int pinVirtualPage(int vpn, boolean isUserWrite) {
    int ppn = super.pinVirtualPage(vpn, isUserWrite);
    if (ppn >= 0) {
      if (isUserWrite) {
        IPT[ppn].pinCount = true;
      }
    } else {
      if (vpn < 0 || vpn >= pageTable.length) return -1;

      if (!pageTable[vpn].valid) {
        allocatePage(vpn);
      }
      ppn = super.pinVirtualPage(vpn, isUserWrite);
      if (isUserWrite) {
        IPT[ppn].pinCount = true;
      }
    }
    return ppn;
  }

  protected void unpinVirtualPage(int vpn) {
    int ppn = super.pinVirtualPage(vpn, false);
    if (ppn >= 0) {
      IPT[ppn].pinCount = false;
      unpinnedPageLock.acquire();
      unpinnedPage.wake();
      unpinnedPageLock.release();
    }
  }

  public void handleTLBMiss() {
    int vaddr = Machine.processor().readRegister(Processor.regBadVAddr);
    int vpn = Processor.pageFromAddress(vaddr);
    allocatePage(vpn);
  }

  private void syncTLB() {
    for (int i = 0; i < Machine.processor().getTLBSize(); i++) {

      TranslationEntry tE = Machine.processor().readTLBEntry(i);
      if (tE.valid) {
        pageTable[tE.vpn].dirty = tE.dirty;
        pageTable[tE.vpn].used = tE.used;
      }
    }
  }

  private void allocatePage(int vpn) {
    // System.out.println("TLBMiss: VPN:" + vpn + " of Process " + processID);
    TranslationEntry t = pageTable[vpn];
    if (!t.valid) {
      // System.out.println("Allocating VPN: " + t.vpn);
      lock.acquire();
      if (UserKernel.freePages.size() > 0) {
        int ppn = ((Integer) UserKernel.freePages.removeFirst()).intValue();
        t.valid = true;
        if (!t.dirty) {
          t.ppn = ppn;
          IPT[ppn] = new IPTEntry();
          IPT[ppn].process = this;
          IPT[ppn].tE = t;

          boolean isCoffSection = false;
          for (int s = 0; s < coff.getNumSections(); s++) {
            if (isCoffSection) {
              break;
            }
            CoffSection section = coff.getSection(s);
            if (section.getFirstVPN() > t.vpn) {
              continue;
            }
            for (int i = 0; i < section.getLength(); i++) {
              int svpn = section.getFirstVPN() + i;
              if (svpn == t.vpn) {
                section.loadPage(i, pinVirtualPage(t.vpn, false));
                //                System.out.println("VPN \"" + t.vpn + "\" is a COFF section");
                // System.out.println("COFF section loaded into VPN \"" + t.vpn + "\" PPN \"" +
                // t.ppn + "\"");
                isCoffSection = true;
                break;
              }
            }
          }

          if (!isCoffSection) {
            //            System.out.println("VPN \"" + t.vpn + "\" is NOT a COFF section");
            // System.out.println("PPN \"" + t.ppn + "\" Zero'd Out");
            byte[] data = new byte[Processor.pageSize];
            // writeVirtualMemory(t.vpn, data, 0, Processor.pageSize);
            byte[] memory = Machine.processor().getMemory();
            System.arraycopy(data, 0, memory, t.ppn * Processor.pageSize, Processor.pageSize);
          }
        } else // Dirty
        {
          //          System.out.println("VPN \"" + t.vpn + "\" has been swapped out, swaping into
          // PPN \"" + ppn + "\"");
          int spn = t.ppn;
          byte[] data = new byte[pageSize];
          byte[] memory = Machine.processor().getMemory();
          OpenFile swap = ThreadedKernel.fileSystem.open(".Nachos.swp", false);
          swap.read(spn * pageSize, memory, ppn * pageSize, pageSize);
          swap.close();
          t.ppn = ppn;
          // System.out.println(spn);
          freeSwapPages.set(spn, true);
          IPT[ppn] = new IPTEntry();
          IPT[ppn].process = this;
          IPT[ppn].tE = t;
          //          System.out.println("SPN \"" + spn + "\" has been swapped in.");
        }
      } else {
        syncTLB();

        // Clock Algorithm
        unpinnedPageLock.acquire();
        int numPinnedPages = 0;
        while (IPT[victim] == null || IPT[victim].tE.used || IPT[victim].pinCount) {
          if (IPT[victim] != null) {
            IPT[victim].tE.used = false;
            // System.out.println("PPN " + victim + " is used.");
          }
          if (IPT[victim].pinCount) {
            numPinnedPages++;
            // System.out.println("PPN " + victim + " is pinned.");
          }
          if (numPinnedPages == IPT.length) unpinnedPage.sleep();
          //          System.out.println("This Process is " + this.processID + " and the IPT's
          // process is " + IPT[victim].processID);
          victim = (victim + 1) % Machine.processor().getNumPhysPages();
        }
        // System.out.println("PPN " + victim + " will be evicted.");
        unpinnedPageLock.release();
        int evict = IPT[victim].tE.vpn;
        VMProcess evictedOwner = IPT[victim].process;
        int evictedPPN = victim;
        victim = (victim + 1) % Machine.processor().getNumPhysPages();

        // System.out.println("Not enough pysical memory, evicting VPN \"" + evict + "\" of process
        // " + evictedOwner.processID);
        // Swap Files
        if (evictedOwner.isDirty(evict)) {
          // System.out.println("VPN \"" + evict + "\" is dirty, swapping out.");
          OpenFile swap = ThreadedKernel.fileSystem.open(".Nachos.swp", false);
          int spn = 0;
          for (spn = 0; spn < freeSwapPages.size(); spn++) {
            if (freeSwapPages.get(spn)) {
              break;
            }
          }
          if (spn == freeSwapPages.size()) {
            freeSwapPages.add(false);
          } else {
            freeSwapPages.set(spn, false);
          }
          UserKernel.freePages.add(evictedPPN);
          byte[] memory = Machine.processor().getMemory();
          swap.write(spn * pageSize, memory, evictedPPN * pageSize, pageSize);
          swap.close();
          if (evictedOwner.processID != this.processID) {
            evictedOwner.evict(evict, spn);
          } else {
            pageTable[evict].ppn = spn;
            pageTable[evict].valid = false;
          }
          for (int i = 0; i < Machine.processor().getTLBSize(); i++) {
            TranslationEntry tE = Machine.processor().readTLBEntry(i);
            if (tE.vpn == evict && tE.ppn == evictedPPN) {
              //              System.out.println("TLBEntry " + i + " invalid");
              tE.valid = false;
              Machine.processor().writeTLBEntry(i, tE);
            }
          }
          lock.release();

          //          System.out.println("VPN \"" + evict + "\" of Process " + this.processID + "
          // swapped out to SPN \"" + spn + "\".");
          // Machine.processor().readTLBEntry(evict).valid = false;
          allocatePage(vpn);
          return;
        } else {
          if (evictedOwner.processID != this.processID) {
            // System.out.println("Evicted VPN " + evict + " of Process " + this.processID);
            evictedOwner.evict(evict);
          } else {
            pageTable[evict].valid = false;
          }
          UserKernel.freePages.add(evictedPPN);
          for (int i = 0; i < Machine.processor().getTLBSize(); i++) {
            TranslationEntry tE = Machine.processor().readTLBEntry(i);
            if (tE.vpn == evict && tE.ppn == evictedPPN) {
              //              System.out.println("TLBEntry " + i + " invalid");
              tE.valid = false;
              Machine.processor().writeTLBEntry(i, tE);
            }
          }
          lock.release();
          allocatePage(vpn);
          return;
        }
      }
      lock.release();
    }
    for (int i = 0; i < Machine.processor().getTLBSize(); i++) {
      TranslationEntry tE = Machine.processor().readTLBEntry(i);
      if (!tE.valid) {
        // System.out.println("Evicted TLBEntry " + i);
        Machine.processor().writeTLBEntry(i, t);
        // System.out.println(t.valid);
        return;
      }
    }
    syncTLB();
    int evictedEntry = (int) (Math.random() * Machine.processor().getTLBSize());
    TranslationEntry tE = Machine.processor().readTLBEntry(evictedEntry);
    pageTable[tE.vpn] = tE;
    Machine.processor().writeTLBEntry(evictedEntry, t);
    // System.out.println("Evicted TLBEntry " + evictedEntry + " randomly");
    // System.out.println("TE entered is VPN \"" + t.vpn + "\" PPN \"" + t.ppn + "\"");

  }

  public void evict(int vpn, int spn) {
    pageTable[vpn].ppn = spn;
    pageTable[vpn].valid = false;
  }

  public void evict(int vpn) {
    pageTable[vpn].valid = false;
  }

  public boolean isDirty(int vpn) {
    return pageTable[vpn].dirty;
  }

  private static final int pageSize = Processor.pageSize;

  private static final char dbgProcess = 'a';

  private static final char dbgVM = 'v';

  private static int victim = 0;

  private static LinkedList<Boolean> freeSwapPages = new LinkedList<Boolean>();

  private static IPTEntry[] IPT = new IPTEntry[Machine.processor().getNumPhysPages()];

  private class IPTEntry {
    public VMProcess process;
    public TranslationEntry tE;
    public boolean pinCount;
  }

  public static Lock unpinnedPageLock = new Lock();
  public static Condition unpinnedPage = new Condition(unpinnedPageLock);

  public static Lock lock = new Lock();

  private int[] savedState = new int[Machine.processor().getTLBSize()];
}