private void extractArguments(String[] args) { String testArgsString = Config.getString("AutoGrader.testArgs"); if (testArgsString == null) { testArgsString = ""; } for (int i = 0; i < args.length; ) { String arg = args[i++]; if (arg.length() > 0 && arg.charAt(0) == '-') { if (arg.equals("-#")) { Lib.assertTrue(i < args.length, "-# switch missing argument"); testArgsString = args[i++]; } } } StringTokenizer st = new StringTokenizer(testArgsString, ",\n\t\f\r"); while (st.hasMoreTokens()) { StringTokenizer pair = new StringTokenizer(st.nextToken(), "="); Lib.assertTrue(pair.hasMoreTokens(), "test argument missing key"); String key = pair.nextToken(); Lib.assertTrue(pair.hasMoreTokens(), "test argument missing value"); String value = pair.nextToken(); testArgs.put(key, value); } }
/** * Context switch between the current TCB and this TCB. This TCB will become the new current TCB. * It is acceptable for this TCB to be the current TCB. */ public void contextSwitch() { /* Probably unnecessary sanity check: we make sure that the current * thread is bound to the current TCB. This check can only fail if * non-Nachos threads invoke start(). */ Lib.assertTrue(currentTCB.javaThread == Thread.currentThread()); // make sure AutoGrader.runningThread() called associateThread() Lib.assertTrue(currentTCB.associated); currentTCB.associated = false; // can't switch from a TCB to itself if (this == currentTCB) return; /* There are some synchronization concerns here. As soon as we wake up * the next thread, we cannot assume anything about static variables, * or about any TCB's state. Therefore, before waking up the next * thread, we must latch the value of currentTCB, and set its running * flag to false (so that, in case we get interrupted before we call * yield(), the interrupt will set the running flag and yield() won't * block). */ TCB previous = currentTCB; previous.running = false; this.interrupt(); previous.yield(); }
private void associateThread(KThread thread) { // make sure AutoGrader.runningThread() gets called only once per // context switch Lib.assertTrue(!associated); associated = true; Lib.assertTrue(thread != null); if (nachosThread != null) Lib.assertTrue(thread == nachosThread); else nachosThread = thread; }
/** * Destroy this TCB. This TCB must not be in use by the current thread. This TCB must also have * been authorized to be destroyed by the autograder. */ public void destroy() { // make sure the current TCB is correct Lib.assertTrue(currentTCB != null && currentTCB.javaThread == Thread.currentThread()); // can't destroy current thread Lib.assertTrue(this != currentTCB); // thread must have started but not be destroyed yet Lib.assertTrue(javaThread != null && !done); // ensure AutoGrader.finishingCurrentThread() called authorizeDestroy() Lib.assertTrue(nachosThread == toBeDestroyed); toBeDestroyed = null; this.done = true; currentTCB.running = false; this.interrupt(); currentTCB.waitForInterrupt(); this.javaThread = null; }
/** * Nachos main entry point. * * @param args the command line arguments. */ public static void main(final String[] args) { System.out.print("nachos 5.0j initializing..."); Lib.assertTrue(Machine.args == null); Machine.args = args; processArgs(); Config.load(configFileName); // get the current directory (.) baseDirectory = new File(new File("").getAbsolutePath()); // get the nachos directory (./nachos) nachosDirectory = new File(baseDirectory, "nachos"); String testDirectoryName = Config.getString("FileSystem.testDirectory"); // get the test directory if (testDirectoryName != null) { testDirectory = new File(testDirectoryName); } else { // use ../test testDirectory = new File(baseDirectory.getParentFile(), "test"); } securityManager = new NachosSecurityManager(testDirectory); privilege = securityManager.getPrivilege(); privilege.machine = new MachinePrivilege(); TCB.givePrivilege(privilege); privilege.stats = stats; securityManager.enable(); createDevices(); checkUserClasses(); autoGrader = (AutoGrader) Lib.constructObject(autoGraderClassName); new TCB() .start( new Runnable() { public void run() { autoGrader.start(privilege); } }); }
private void threadroot() { // this should be running the current thread Lib.assertTrue(javaThread == Thread.currentThread()); if (!isFirstTCB) { /* start() is waiting for us to wake it up, signalling that it's OK * to context switch to us. We leave the running flag false so that * we'll still run if a context switch happens before we go to * sleep. All we have to do is wake up the current TCB and then * wait to get woken up by contextSwitch() or destroy(). */ currentTCB.interrupt(); this.yield(); } else { /* start() called us directly, so we just need to initialize * a couple things. */ currentTCB = this; running = true; } try { target.run(); // no way out of here without going throw one of the catch blocks Lib.assertNotReached(); } catch (ThreadDeath e) { // make sure this TCB is being destroyed properly if (!done) { System.out.print("\nTCB terminated improperly!\n"); privilege.exit(1); } runningThreads.removeElement(this); if (runningThreads.isEmpty()) privilege.exit(0); } catch (Throwable e) { System.out.print("\n"); e.printStackTrace(); runningThreads.removeElement(this); if (runningThreads.isEmpty()) privilege.exit(1); else die(); } }
/** * 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(); }
private static void processArgs() { for (int i = 0; i < args.length; ) { String arg = args[i++]; if (arg.length() > 0 && arg.charAt(0) == '-') { if (arg.equals("-d")) { Lib.assertTrue(i < args.length, "switch without argument"); Lib.enableDebugFlags(args[i++]); } else if (arg.equals("-h")) { System.out.print(help); System.exit(1); } else if (arg.equals("-m")) { Lib.assertTrue(i < args.length, "switch without argument"); try { numPhysPages = Integer.parseInt(args[i++]); } catch (NumberFormatException e) { Lib.assertNotReached("bad value for -m switch"); } } else if (arg.equals("-s")) { Lib.assertTrue(i < args.length, "switch without argument"); try { randomSeed = Long.parseLong(args[i++]); } catch (NumberFormatException e) { Lib.assertNotReached("bad value for -s switch"); } } else if (arg.equals("-x")) { Lib.assertTrue(i < args.length, "switch without argument"); shellProgramName = args[i++]; } else if (arg.equals("-z")) { System.out.print(copyright); System.exit(1); } // these switches are reserved for the autograder else if (arg.equals("-[]")) { Lib.assertTrue(i < args.length, "switch without argument"); configFileName = args[i++]; } else if (arg.equals("--")) { Lib.assertTrue(i < args.length, "switch without argument"); autoGraderClassName = args[i++]; } } } Lib.seedRandom(randomSeed); }
/** * Notify the autograder that a user program executed a syscall instruction. * * @param privilege proves the authenticity of this call. * @return <tt>true</tt> if the kernel exception handler should be called. */ public boolean exceptionHandler(Privilege privilege) { Lib.assertTrue(privilege == this.privilege, "security violation"); return true; }
private void delay() { long time = Machine.timer().getTime(); int amount = 1000; ThreadedKernel.alarm.waitUntil(amount); Lib.assertTrue(Machine.timer().getTime() >= time + amount); }
String getStringArgument(String key) { String value = (String) testArgs.get(key); Lib.assertTrue(value != null, "getStringArgument(" + key + ") failed to find key"); return value; }
/** * Request permission to receive a packet. The autograder can use this to drop packets very * selectively. * * @param privilege proves the authenticity of this call. * @return <tt>true</tt> if the packet should be delivered to the kernel. */ public boolean canReceivePacket(Privilege privilege) { Lib.assertTrue(privilege == this.privilege, "security violation"); return true; }
private static void authorizeDestroy(KThread thread) { // make sure AutoGrader.finishingThread() gets called only once per // destroy Lib.assertTrue(toBeDestroyed == null); toBeDestroyed = thread; }
/** * Return the name of the shell program that a user-programming kernel must run. Make sure * <tt>UserKernel.run()</tt> <i>always</i> uses this method to decide which program to run. * * @return the name of the shell program to run. */ public static String getShellProgramName() { if (shellProgramName == null) shellProgramName = Config.getString("Kernel.shellProgram"); Lib.assertTrue(shellProgramName != null); return shellProgramName; }
/** * Notify the autograder that a timer interrupt occurred and was handled by software if a timer * interrupt handler was installed. Called by the hardware timer. * * @param privilege proves the authenticity of this call. * @param time the actual time at which the timer interrupt was issued. */ public void timerInterrupt(Privilege privilege, long time) { Lib.assertTrue(privilege == this.privilege, "security violation"); }
void level(int level) { this.level++; Lib.assertTrue(level == this.level, "level() advanced more than one step: test jumped ahead"); if (level == targetLevel) done(); }
/** * Causes the thread represented by this TCB to begin execution. The specified target is run in * the thread. */ public void start(Runnable target) { /* We will not use synchronization here, because we're assuming that * either this is the first call to start(), or we're being called in * the context of another TCB. Since we only allow one TCB to run at a * time, no synchronization is necessary. * * The only way this assumption could be broken is if one of our * non-Nachos threads used the TCB code. */ /* Make sure this TCB has not already been started. If done is false, * then destroy() has not yet set javaThread back to null, so we can * use javaThread as a reliable indicator of whether or not start() has * already been invoked. */ Lib.assertTrue(javaThread == null && !done); /* Make sure there aren't too many running TCBs already. This * limitation exists in an effort to prevent wild thread usage. */ Lib.assertTrue(runningThreads.size() < maxThreads); isFirstTCB = (currentTCB == null); /* Probably unnecessary sanity check: if this is not the first TCB, we * make sure that the current thread is bound to the current TCB. This * check can only fail if non-Nachos threads invoke start(). */ if (!isFirstTCB) Lib.assertTrue(currentTCB.javaThread == Thread.currentThread()); /* At this point all checks are complete, so we go ahead and start the * TCB. Whether or not this is the first TCB, it gets added to * runningThreads, and we save the target closure. */ runningThreads.add(this); this.target = target; if (!isFirstTCB) { /* If this is not the first TCB, we have to make a new Java thread * to run it. Creating Java threads is a privileged operation. */ tcbTarget = new Runnable() { public void run() { threadroot(); } }; privilege.doPrivileged( new Runnable() { public void run() { javaThread = new Thread(tcbTarget); } }); /* The Java thread hasn't yet started, but we need to get it * blocking in yield(). We do this by temporarily turning off the * current TCB, starting the new Java thread, and waiting for it * to wake us up from threadroot(). Once the new TCB wakes us up, * it's safe to context switch to the new TCB. */ currentTCB.running = false; this.javaThread.start(); currentTCB.waitForInterrupt(); } else { /* This is the first TCB, so we don't need to make a new Java * thread to run it; we just steal the current Java thread. */ javaThread = Thread.currentThread(); /* All we have to do now is invoke threadroot() directly. */ threadroot(); } }
public void associateThread(KThread thread) { Lib.assertTrue(currentTCB != null); currentTCB.associateThread(thread); }
/** * Return the name of the process class that the kernel should use. In the multi-programming * project, returns <tt>nachos.userprog.UserProcess</tt>. In the VM project, returns * <tt>nachos.vm.VMProcess</tt>. In the networking project, returns * <tt>nachos.network.NetProcess</tt>. * * @return the name of the process class that the kernel should use. * @see nachos.userprog.UserKernel#run * @see nachos.userprog.UserProcess * @see nachos.vm.VMProcess * @see nachos.network.NetProcess */ public static String getProcessClassName() { if (processClassName == null) processClassName = Config.getString("Kernel.processClassName"); Lib.assertTrue(processClassName != null); return processClassName; }
/** * Notify the autograder that <tt>Processor.run()</tt> was invoked. This can be used to simulate * user programs. * * @param privilege proves the authenticity of this call. */ public void runProcessor(Privilege privilege) { Lib.assertTrue(privilege == this.privilege, "security violation"); }