00001 // interrupt.cc 00002 // Routines to simulate hardware interrupts. 00003 // 00004 // The hardware provides a routine (SetLevel) to enable or disable 00005 // interrupts. 00006 // 00007 // In order to emulate the hardware, we need to keep track of all 00008 // interrupts the hardware devices would cause, and when they 00009 // are supposed to occur. 00010 // 00011 // This module also keeps track of simulated time. Time advances 00012 // only when the following occur: 00013 // interrupts are re-enabled 00014 // a user instruction is executed 00015 // there is nothing in the ready queue 00016 // 00017 // DO NOT CHANGE -- part of the machine emulation 00018 // 00019 // Copyright (c) 1992-1993 The Regents of the University of California. 00020 // All rights reserved. See copyright.h for copyright notice and limitation 00021 // of liability and disclaimer of warranty provisions. 00022 00023 #include "copyright.h" 00024 #include "interrupt.h" 00025 #include "system.h" 00026 00027 // String definitions for debugging messages 00028 00029 static char *intLevelNames[] = { "off", "on"}; 00030 static char *intTypeNames[] = { "timer", "disk", "console write", 00031 "console read", "network send", "network recv"}; 00032 00033 //---------------------------------------------------------------------- 00034 // PendingInterrupt::PendingInterrupt 00035 // Initialize a hardware device interrupt that is to be scheduled 00036 // to occur in the near future. 00037 // 00038 // "func" is the procedure to call when the interrupt occurs 00039 // "param" is the argument to pass to the procedure 00040 // "time" is when (in simulated time) the interrupt is to occur 00041 // "kind" is the hardware device that generated the interrupt 00042 //---------------------------------------------------------------------- 00043 00044 PendingInterrupt::PendingInterrupt(VoidFunctionPtr func, int param, int time, 00045 IntType kind) 00046 { 00047 handler = func; 00048 arg = param; 00049 when = time; 00050 type = kind; 00051 } 00052 00053 //---------------------------------------------------------------------- 00054 // Interrupt::Interrupt 00055 // Initialize the simulation of hardware device interrupts. 00056 // 00057 // Interrupts start disabled, with no interrupts pending, etc. 00058 //---------------------------------------------------------------------- 00059 00060 Interrupt::Interrupt() 00061 { 00062 level = IntOff; 00063 pending = new List(); 00064 inHandler = FALSE; 00065 yieldOnReturn = FALSE; 00066 status = SystemMode; 00067 } 00068 00069 //---------------------------------------------------------------------- 00070 // Interrupt::~Interrupt 00071 // De-allocate the data structures needed by the interrupt simulation. 00072 //---------------------------------------------------------------------- 00073 00074 Interrupt::~Interrupt() 00075 { 00076 while (!pending->IsEmpty()) 00077 delete pending->Remove(); 00078 delete pending; 00079 } 00080 00081 //---------------------------------------------------------------------- 00082 // Interrupt::ChangeLevel 00083 // Change interrupts to be enabled or disabled, without advancing 00084 // the simulated time (normally, enabling interrupts advances the time). 00085 00086 //---------------------------------------------------------------------- 00087 // Interrupt::ChangeLevel 00088 // Change interrupts to be enabled or disabled, without advancing 00089 // the simulated time (normally, enabling interrupts advances the time). 00090 // 00091 // Used internally. 00092 // 00093 // "old" -- the old interrupt status 00094 // "now" -- the new interrupt status 00095 //---------------------------------------------------------------------- 00096 void 00097 Interrupt::ChangeLevel(IntStatus old, IntStatus now) 00098 { 00099 level = now; 00100 DEBUG('i',"\tinterrupts: %s -> %s\n",intLevelNames[old],intLevelNames[now]); 00101 } 00102 00103 //---------------------------------------------------------------------- 00104 // Interrupt::SetLevel 00105 // Change interrupts to be enabled or disabled, and if interrupts 00106 // are being enabled, advance simulated time by calling OneTick(). 00107 // 00108 // Returns: 00109 // The old interrupt status. 00110 // Parameters: 00111 // "now" -- the new interrupt status 00112 //---------------------------------------------------------------------- 00113 00114 IntStatus 00115 Interrupt::SetLevel(IntStatus now) 00116 { 00117 IntStatus old = level; 00118 00119 ASSERT((now == IntOff) || (inHandler == FALSE));// interrupt handlers are 00120 // prohibited from enabling 00121 // interrupts 00122 00123 ChangeLevel(old, now); // change to new state 00124 if ((now == IntOn) && (old == IntOff)) 00125 OneTick(); // advance simulated time 00126 return old; 00127 } 00128 00129 //---------------------------------------------------------------------- 00130 // Interrupt::Enable 00131 // Turn interrupts on. Who cares what they used to be? 00132 // Used in ThreadRoot, to turn interrupts on when first starting up 00133 // a thread. 00134 //---------------------------------------------------------------------- 00135 void 00136 Interrupt::Enable() 00137 { 00138 (void) SetLevel(IntOn); 00139 } 00140 00141 //---------------------------------------------------------------------- 00142 // Interrupt::OneTick 00143 // Advance simulated time and check if there are any pending 00144 // interrupts to be called. 00145 // 00146 // Two things can cause OneTick to be called: 00147 // interrupts are re-enabled 00148 // a user instruction is executed 00149 //---------------------------------------------------------------------- 00150 void 00151 Interrupt::OneTick() 00152 { 00153 MachineStatus old = status; 00154 00155 // advance simulated time 00156 if (status == SystemMode) { 00157 stats->totalTicks += SystemTick; 00158 stats->systemTicks += SystemTick; 00159 } else { // USER_PROGRAM 00160 stats->totalTicks += UserTick; 00161 stats->userTicks += UserTick; 00162 } 00163 DEBUG('i', "\n== Tick %d ==\n", stats->totalTicks); 00164 00165 // check any pending interrupts are now ready to fire 00166 ChangeLevel(IntOn, IntOff); // first, turn off interrupts 00167 // (interrupt handlers run with 00168 // interrupts disabled) 00169 while (CheckIfDue(FALSE)) // check for pending interrupts 00170 ; 00171 ChangeLevel(IntOff, IntOn); // re-enable interrupts 00172 if (yieldOnReturn) { // if the timer device handler asked 00173 // for a context switch, ok to do it now 00174 yieldOnReturn = FALSE; 00175 status = SystemMode; // yield is a kernel routine 00176 currentThread->Yield(); 00177 status = old; 00178 } 00179 } 00180 00181 //---------------------------------------------------------------------- 00182 // Interrupt::YieldOnReturn 00183 // Called from within an interrupt handler, to cause a context switch 00184 // (for example, on a time slice) in the interrupted thread, 00185 // when the handler returns. 00186 // 00187 // We can't do the context switch here, because that would switch 00188 // out the interrupt handler, and we want to switch out the 00189 // interrupted thread. 00190 //---------------------------------------------------------------------- 00191 00192 void 00193 Interrupt::YieldOnReturn() 00194 { 00195 ASSERT(inHandler == TRUE); 00196 yieldOnReturn = TRUE; 00197 } 00198 00199 //---------------------------------------------------------------------- 00200 // Interrupt::Idle 00201 // Routine called when there is nothing in the ready queue. 00202 // 00203 // Since something has to be running in order to put a thread 00204 // on the ready queue, the only thing to do is to advance 00205 // simulated time until the next scheduled hardware interrupt. 00206 // 00207 // If there are no pending interrupts, stop. There's nothing 00208 // more for us to do. 00209 //---------------------------------------------------------------------- 00210 void 00211 Interrupt::Idle() 00212 { 00213 DEBUG('i', "Machine idling; checking for interrupts.\n"); 00214 status = IdleMode; 00215 if (CheckIfDue(TRUE)) { // check for any pending interrupts 00216 while (CheckIfDue(FALSE)) // check for any other pending 00217 ; // interrupts 00218 yieldOnReturn = FALSE; // since there's nothing in the 00219 // ready queue, the yield is automatic 00220 status = SystemMode; 00221 return; // return in case there's now 00222 // a runnable thread 00223 } 00224 00225 // if there are no pending interrupts, and nothing is on the ready 00226 // queue, it is time to stop. If the console or the network is 00227 // operating, there are *always* pending interrupts, so this code 00228 // is not reached. Instead, the halt must be invoked by the user program. 00229 00230 DEBUG('i', "Machine idle. No interrupts to do.\n"); 00231 printf("No threads ready or runnable, and no pending interrupts.\n"); 00232 printf("Assuming the program completed.\n"); 00233 Halt(); 00234 } 00235 00236 //---------------------------------------------------------------------- 00237 // Interrupt::Halt 00238 // Shut down Nachos cleanly, printing out performance statistics. 00239 //---------------------------------------------------------------------- 00240 void 00241 Interrupt::Halt() 00242 { 00243 printf("Machine halting!\n\n"); 00244 stats->Print(); 00245 Cleanup(); // Never returns. 00246 } 00247 00248 //---------------------------------------------------------------------- 00249 // Interrupt::Schedule 00250 // Arrange for the CPU to be interrupted when simulated time 00251 // reaches "now + when". 00252 // 00253 // Implementation: just put it on a sorted list. 00254 // 00255 // NOTE: the Nachos kernel should not call this routine directly. 00256 // Instead, it is only called by the hardware device simulators. 00257 // 00258 // "handler" is the procedure to call when the interrupt occurs 00259 // "arg" is the argument to pass to the procedure 00260 // "fromNow" is how far in the future (in simulated time) the 00261 // interrupt is to occur 00262 // "type" is the hardware device that generated the interrupt 00263 //---------------------------------------------------------------------- 00264 void 00265 Interrupt::Schedule(VoidFunctionPtr handler, int arg, int fromNow, IntType type) 00266 { 00267 int when = stats->totalTicks + fromNow; 00268 PendingInterrupt *toOccur = new PendingInterrupt(handler, arg, when, type); 00269 00270 DEBUG('i', "Scheduling interrupt handler the %s at time = %d\n", 00271 intTypeNames[type], when); 00272 ASSERT(fromNow > 0); 00273 00274 pending->SortedInsert(toOccur, when); 00275 } 00276 00277 //---------------------------------------------------------------------- 00278 // Interrupt::CheckIfDue 00279 // Check if an interrupt is scheduled to occur, and if so, fire it off. 00280 // 00281 // Returns: 00282 // TRUE, if we fired off any interrupt handlers 00283 // Params: 00284 // "advanceClock" -- if TRUE, there is nothing in the ready queue, 00285 // so we should simply advance the clock to when the next 00286 // pending interrupt would occur (if any). If the pending 00287 // interrupt is just the time-slice daemon, however, then 00288 // we're done! 00289 //---------------------------------------------------------------------- 00290 bool 00291 Interrupt::CheckIfDue(bool advanceClock) 00292 { 00293 MachineStatus old = status; 00294 int when; 00295 00296 ASSERT(level == IntOff); // interrupts need to be disabled, 00297 // to invoke an interrupt handler 00298 if (DebugIsEnabled('i')) 00299 DumpState(); 00300 PendingInterrupt *toOccur = 00301 (PendingInterrupt *)pending->SortedRemove(&when); 00302 00303 if (toOccur == NULL) // no pending interrupts 00304 return FALSE; 00305 00306 if (advanceClock && when > stats->totalTicks) { // advance the clock 00307 stats->idleTicks += (when - stats->totalTicks); 00308 stats->totalTicks = when; 00309 } else if (when > stats->totalTicks) { // not time yet, put it back 00310 pending->SortedInsert(toOccur, when); 00311 return FALSE; 00312 } 00313 00314 // Check if there is nothing more to do, and if so, quit 00315 if ((status == IdleMode) && (toOccur->type == TimerInt) 00316 && pending->IsEmpty()) { 00317 pending->SortedInsert(toOccur, when); 00318 return FALSE; 00319 } 00320 00321 DEBUG('i', "Invoking interrupt handler for the %s at time %d\n", 00322 intTypeNames[toOccur->type], toOccur->when); 00323 #ifdef USER_PROGRAM 00324 if (machine != NULL) 00325 machine->DelayedLoad(0, 0); 00326 #endif 00327 inHandler = TRUE; 00328 status = SystemMode; // whatever we were doing, 00329 // we are now going to be 00330 // running in the kernel 00331 (*(toOccur->handler))(toOccur->arg); // call the interrupt handler 00332 status = old; // restore the machine status 00333 inHandler = FALSE; 00334 delete toOccur; 00335 return TRUE; 00336 } 00337 00338 //---------------------------------------------------------------------- 00339 // PrintPending 00340 // Print information about an interrupt that is scheduled to occur. 00341 // When, where, why, etc. 00342 //---------------------------------------------------------------------- 00343 00344 static void 00345 PrintPending(int arg) 00346 { 00347 PendingInterrupt *pend = (PendingInterrupt *)arg; 00348 00349 printf("Interrupt handler %s, scheduled at %d\n", 00350 intTypeNames[pend->type], pend->when); 00351 } 00352 00353 //---------------------------------------------------------------------- 00354 // DumpState 00355 // Print the complete interrupt state - the status, and all interrupts 00356 // that are scheduled to occur in the future. 00357 //---------------------------------------------------------------------- 00358 00359 void 00360 Interrupt::DumpState() 00361 { 00362 printf("Time: %d, interrupts %s\n", stats->totalTicks, 00363 intLevelNames[level]); 00364 printf("Pending interrupts:\n"); 00365 fflush(stdout); 00366 pending->Mapcar(PrintPending); 00367 printf("End of pending interrupts\n"); 00368 fflush(stdout); 00369 }
1.2.14 written by Dimitri van Heesch,
© 1997-2002