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-1996 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 "main.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", 00032 "network recv"}; 00033 00034 //---------------------------------------------------------------------- 00035 // PendingInterrupt::PendingInterrupt 00036 // Initialize a hardware device interrupt that is to be scheduled 00037 // to occur in the near future. 00038 // 00039 // "callOnInt" is the object to call when the interrupt occurs 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(CallBackObj *callOnInt, 00045 int time, IntType kind) 00046 { 00047 callOnInterrupt = callOnInt; 00048 when = time; 00049 type = kind; 00050 } 00051 00052 //---------------------------------------------------------------------- 00053 // PendingCompare 00054 // Compare to interrupts based on which should occur first. 00055 //---------------------------------------------------------------------- 00056 00057 static int 00058 PendingCompare (PendingInterrupt *x, PendingInterrupt *y) 00059 { 00060 if (x->when < y->when) { return -1; } 00061 else if (x->when > y->when) { return 1; } 00062 else { return 0; } 00063 } 00064 00065 //---------------------------------------------------------------------- 00066 // Interrupt::Interrupt 00067 // Initialize the simulation of hardware device interrupts. 00068 // 00069 // Interrupts start disabled, with no interrupts pending, etc. 00070 //---------------------------------------------------------------------- 00071 00072 Interrupt::Interrupt() 00073 { 00074 level = IntOff; 00075 pending = new SortedList<PendingInterrupt *>(PendingCompare); 00076 inHandler = FALSE; 00077 yieldOnReturn = FALSE; 00078 status = SystemMode; 00079 } 00080 00081 //---------------------------------------------------------------------- 00082 // Interrupt::~Interrupt 00083 // De-allocate the data structures needed by the interrupt simulation. 00084 //---------------------------------------------------------------------- 00085 00086 Interrupt::~Interrupt() 00087 { 00088 while (!pending->IsEmpty()) { 00089 delete pending->RemoveFront(); 00090 } 00091 delete pending; 00092 } 00093 00094 //---------------------------------------------------------------------- 00095 // Interrupt::ChangeLevel 00096 // Change interrupts to be enabled or disabled, without advancing 00097 // the simulated time (normally, enabling interrupts advances the time). 00098 // 00099 // Used internally. 00100 // 00101 // "old" -- the old interrupt status 00102 // "now" -- the new interrupt status 00103 //---------------------------------------------------------------------- 00104 00105 void 00106 Interrupt::ChangeLevel(IntStatus old, IntStatus now) 00107 { 00108 level = now; 00109 DEBUG(dbgInt, "\tinterrupts: " << intLevelNames[old] << " -> " << intLevelNames[now]); 00110 } 00111 00112 //---------------------------------------------------------------------- 00113 // Interrupt::SetLevel 00114 // Change interrupts to be enabled or disabled, and if interrupts 00115 // are being enabled, advance simulated time by calling OneTick(). 00116 // 00117 // Returns: 00118 // The old interrupt status. 00119 // Parameters: 00120 // "now" -- the new interrupt status 00121 //---------------------------------------------------------------------- 00122 00123 IntStatus 00124 Interrupt::SetLevel(IntStatus now) 00125 { 00126 IntStatus old = level; 00127 00128 // interrupt handlers are prohibited from enabling interrupts 00129 ASSERT((now == IntOff) || (inHandler == FALSE)); 00130 00131 ChangeLevel(old, now); // change to new state 00132 if ((now == IntOn) && (old == IntOff)) { 00133 OneTick(); // advance simulated time 00134 } 00135 return old; 00136 } 00137 00138 //---------------------------------------------------------------------- 00139 // Interrupt::OneTick 00140 // Advance simulated time and check if there are any pending 00141 // interrupts to be called. 00142 // 00143 // Two things can cause OneTick to be called: 00144 // interrupts are re-enabled 00145 // a user instruction is executed 00146 //---------------------------------------------------------------------- 00147 void 00148 Interrupt::OneTick() 00149 { 00150 MachineStatus oldStatus = status; 00151 Statistics *stats = kernel->stats; 00152 00153 // advance simulated time 00154 if (status == SystemMode) { 00155 stats->totalTicks += SystemTick; 00156 stats->systemTicks += SystemTick; 00157 } else { 00158 stats->totalTicks += UserTick; 00159 stats->userTicks += UserTick; 00160 } 00161 DEBUG(dbgInt, "== Tick " << stats->totalTicks << " =="); 00162 00163 // check any pending interrupts are now ready to fire 00164 ChangeLevel(IntOn, IntOff); // first, turn off interrupts 00165 // (interrupt handlers run with 00166 // interrupts disabled) 00167 CheckIfDue(FALSE); // check for pending interrupts 00168 ChangeLevel(IntOff, IntOn); // re-enable interrupts 00169 if (yieldOnReturn) { // if the timer device handler asked 00170 // for a context switch, ok to do it now 00171 yieldOnReturn = FALSE; 00172 status = SystemMode; // yield is a kernel routine 00173 kernel->currentThread->Yield(); 00174 status = oldStatus; 00175 } 00176 } 00177 00178 //---------------------------------------------------------------------- 00179 // Interrupt::YieldOnReturn 00180 // Called from within an interrupt handler, to cause a context switch 00181 // (for example, on a time slice) in the interrupted thread, 00182 // when the handler returns. 00183 // 00184 // We can't do the context switch here, because that would switch 00185 // out the interrupt handler, and we want to switch out the 00186 // interrupted thread. 00187 //---------------------------------------------------------------------- 00188 00189 void 00190 Interrupt::YieldOnReturn() 00191 { 00192 ASSERT(inHandler == TRUE); 00193 yieldOnReturn = TRUE; 00194 } 00195 00196 //---------------------------------------------------------------------- 00197 // Interrupt::Idle 00198 // Routine called when there is nothing in the ready queue. 00199 // 00200 // Since something has to be running in order to put a thread 00201 // on the ready queue, the only thing to do is to advance 00202 // simulated time until the next scheduled hardware interrupt. 00203 // 00204 // If there are no pending interrupts, stop. There's nothing 00205 // more for us to do. 00206 //---------------------------------------------------------------------- 00207 void 00208 Interrupt::Idle() 00209 { 00210 DEBUG(dbgInt, "Machine idling; checking for interrupts."); 00211 status = IdleMode; 00212 if (CheckIfDue(TRUE)) { // check for any pending interrupts 00213 status = SystemMode; 00214 return; // return in case there's now 00215 // a runnable thread 00216 } 00217 00218 // if there are no pending interrupts, and nothing is on the ready 00219 // queue, it is time to stop. If the console or the network is 00220 // operating, there are *always* pending interrupts, so this code 00221 // is not reached. Instead, the halt must be invoked by the user program. 00222 00223 DEBUG(dbgInt, "Machine idle. No interrupts to do."); 00224 cout << "No threads ready or runnable, and no pending interrupts.\n"; 00225 cout << "Assuming the program completed.\n"; 00226 Halt(); 00227 } 00228 00229 //---------------------------------------------------------------------- 00230 // Interrupt::Halt 00231 // Shut down Nachos cleanly, printing out performance statistics. 00232 //---------------------------------------------------------------------- 00233 void 00234 Interrupt::Halt() 00235 { 00236 cerr << "Machine halting!\n\n"; 00237 kernel->stats->Print(); 00238 delete kernel; // Never returns. 00239 } 00240 00241 //---------------------------------------------------------------------- 00242 // Interrupt::Schedule 00243 // Arrange for the CPU to be interrupted when simulated time 00244 // reaches "now + when". 00245 // 00246 // Implementation: just put it on a sorted list. 00247 // 00248 // NOTE: the Nachos kernel should not call this routine directly. 00249 // Instead, it is only called by the hardware device simulators. 00250 // 00251 // "toCall" is the object to call when the interrupt occurs 00252 // "fromNow" is how far in the future (in simulated time) the 00253 // interrupt is to occur 00254 // "type" is the hardware device that generated the interrupt 00255 //---------------------------------------------------------------------- 00256 void 00257 Interrupt::Schedule(CallBackObj *toCall, int fromNow, IntType type) 00258 { 00259 int when = kernel->stats->totalTicks + fromNow; 00260 PendingInterrupt *toOccur = new PendingInterrupt(toCall, when, type); 00261 00262 DEBUG(dbgInt, "Scheduling interrupt handler the " << intTypeNames[type] << " at time = " << when); 00263 ASSERT(fromNow > 0); 00264 00265 pending->Insert(toOccur); 00266 } 00267 00268 //---------------------------------------------------------------------- 00269 // Interrupt::CheckIfDue 00270 // Check if any interrupts are scheduled to occur, and if so, 00271 // fire them off. 00272 // 00273 // Returns: 00274 // TRUE, if we fired off any interrupt handlers 00275 // Params: 00276 // "advanceClock" -- if TRUE, there is nothing in the ready queue, 00277 // so we should simply advance the clock to when the next 00278 // pending interrupt would occur (if any). 00279 //---------------------------------------------------------------------- 00280 bool 00281 Interrupt::CheckIfDue(bool advanceClock) 00282 { 00283 PendingInterrupt *next; 00284 Statistics *stats = kernel->stats; 00285 00286 ASSERT(level == IntOff); // interrupts need to be disabled, 00287 // to invoke an interrupt handler 00288 if (debug->IsEnabled(dbgInt)) { 00289 DumpState(); 00290 } 00291 if (pending->IsEmpty()) { // no pending interrupts 00292 return FALSE; 00293 } 00294 next = pending->Front(); 00295 if (next->when > stats->totalTicks) { 00296 if (!advanceClock) { // not time yet 00297 return FALSE; 00298 } 00299 else { // advance the clock to next interrupt 00300 stats->idleTicks += (next->when - stats->totalTicks); 00301 stats->totalTicks = next->when; 00302 // UDelay(1000L); // rcgood - to stop nachos from spinning. 00303 } 00304 } 00305 00306 DEBUG(dbgInt, "Invoking interrupt handler for the "); 00307 DEBUG(dbgInt, intTypeNames[next->type] << " at time " << next->when); 00308 00309 if (kernel->machine != NULL) { 00310 kernel->machine->DelayedLoad(0, 0); 00311 } 00312 00313 inHandler = TRUE; 00314 do { 00315 next = pending->RemoveFront(); // pull interrupt off list 00316 next->callOnInterrupt->CallBack();// call the interrupt handler 00317 delete next; 00318 } while (!pending->IsEmpty() 00319 && (pending->Front()->when <= stats->totalTicks)); 00320 inHandler = FALSE; 00321 return TRUE; 00322 } 00323 00324 //---------------------------------------------------------------------- 00325 // PrintPending 00326 // Print information about an interrupt that is scheduled to occur. 00327 // When, where, why, etc. 00328 //---------------------------------------------------------------------- 00329 00330 static void 00331 PrintPending (PendingInterrupt *pending) 00332 { 00333 cerr << "Interrupt handler "<< intTypeNames[pending->type]; 00334 cerr << ", scheduled at " << pending->when; 00335 } 00336 00337 //---------------------------------------------------------------------- 00338 // DumpState 00339 // Print the complete interrupt state - the status, and all interrupts 00340 // that are scheduled to occur in the future. 00341 //---------------------------------------------------------------------- 00342 00343 void 00344 Interrupt::DumpState() 00345 { 00346 cerr << "Time: " << kernel->stats->totalTicks; 00347 cerr << ", interrupts " << intLevelNames[level] << "\n"; 00348 cerr << "Pending interrupts:\n"; 00349 pending->Apply(PrintPending); 00350 cerr << "\nEnd of pending interrupts\n"; 00351 }