init: init nachos hw01, should pass jenkins os_group_20_hw job but fail on os_group_20_ta job
This commit is contained in:
361
code/machine/interrupt.cc
Normal file
361
code/machine/interrupt.cc
Normal file
@@ -0,0 +1,361 @@
|
||||
// interrupt.cc
|
||||
// Routines to simulate hardware interrupts.
|
||||
//
|
||||
// The hardware provides a routine (SetLevel) to enable or disable
|
||||
// interrupts.
|
||||
//
|
||||
// In order to emulate the hardware, we need to keep track of all
|
||||
// interrupts the hardware devices would cause, and when they
|
||||
// are supposed to occur.
|
||||
//
|
||||
// This module also keeps track of simulated time. Time advances
|
||||
// only when the following occur:
|
||||
// interrupts are re-enabled
|
||||
// a user instruction is executed
|
||||
// there is nothing in the ready queue
|
||||
//
|
||||
// DO NOT CHANGE -- part of the machine emulation
|
||||
//
|
||||
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#include "copyright.h"
|
||||
#include "interrupt.h"
|
||||
#include "main.h"
|
||||
|
||||
// String definitions for debugging messages
|
||||
|
||||
static char *intLevelNames[] = { "off", "on"};
|
||||
static char *intTypeNames[] = { "timer", "disk", "console write",
|
||||
"console read", "network send",
|
||||
"network recv"};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PendingInterrupt::PendingInterrupt
|
||||
// Initialize a hardware device interrupt that is to be scheduled
|
||||
// to occur in the near future.
|
||||
//
|
||||
// "callOnInt" is the object to call when the interrupt occurs
|
||||
// "time" is when (in simulated time) the interrupt is to occur
|
||||
// "kind" is the hardware device that generated the interrupt
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
PendingInterrupt::PendingInterrupt(CallBackObj *callOnInt,
|
||||
int time, IntType kind)
|
||||
{
|
||||
callOnInterrupt = callOnInt;
|
||||
when = time;
|
||||
type = kind;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PendingCompare
|
||||
// Compare to interrupts based on which should occur first.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static int
|
||||
PendingCompare (PendingInterrupt *x, PendingInterrupt *y)
|
||||
{
|
||||
if (x->when < y->when) { return -1; }
|
||||
else if (x->when > y->when) { return 1; }
|
||||
else { return 0; }
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::Interrupt
|
||||
// Initialize the simulation of hardware device interrupts.
|
||||
//
|
||||
// Interrupts start disabled, with no interrupts pending, etc.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Interrupt::Interrupt()
|
||||
{
|
||||
level = IntOff;
|
||||
pending = new SortedList<PendingInterrupt *>(PendingCompare);
|
||||
inHandler = FALSE;
|
||||
yieldOnReturn = FALSE;
|
||||
status = SystemMode;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::~Interrupt
|
||||
// De-allocate the data structures needed by the interrupt simulation.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Interrupt::~Interrupt()
|
||||
{
|
||||
while (!pending->IsEmpty()) {
|
||||
delete pending->RemoveFront();
|
||||
}
|
||||
delete pending;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::ChangeLevel
|
||||
// Change interrupts to be enabled or disabled, without advancing
|
||||
// the simulated time (normally, enabling interrupts advances the time).
|
||||
//
|
||||
// Used internally.
|
||||
//
|
||||
// "old" -- the old interrupt status
|
||||
// "now" -- the new interrupt status
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Interrupt::ChangeLevel(IntStatus old, IntStatus now)
|
||||
{
|
||||
level = now;
|
||||
DEBUG(dbgInt, "\tinterrupts: " << intLevelNames[old] << " -> " << intLevelNames[now]);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::SetLevel
|
||||
// Change interrupts to be enabled or disabled, and if interrupts
|
||||
// are being enabled, advance simulated time by calling OneTick().
|
||||
//
|
||||
// Returns:
|
||||
// The old interrupt status.
|
||||
// Parameters:
|
||||
// "now" -- the new interrupt status
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
IntStatus
|
||||
Interrupt::SetLevel(IntStatus now)
|
||||
{
|
||||
IntStatus old = level;
|
||||
|
||||
// interrupt handlers are prohibited from enabling interrupts
|
||||
ASSERT((now == IntOff) || (inHandler == FALSE));
|
||||
|
||||
ChangeLevel(old, now); // change to new state
|
||||
if ((now == IntOn) && (old == IntOff)) {
|
||||
OneTick(); // advance simulated time
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::OneTick
|
||||
// Advance simulated time and check if there are any pending
|
||||
// interrupts to be called.
|
||||
//
|
||||
// Two things can cause OneTick to be called:
|
||||
// interrupts are re-enabled
|
||||
// a user instruction is executed
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::OneTick()
|
||||
{
|
||||
MachineStatus oldStatus = status;
|
||||
Statistics *stats = kernel->stats;
|
||||
|
||||
// advance simulated time
|
||||
if (status == SystemMode) {
|
||||
stats->totalTicks += SystemTick;
|
||||
stats->systemTicks += SystemTick;
|
||||
} else {
|
||||
stats->totalTicks += UserTick;
|
||||
stats->userTicks += UserTick;
|
||||
}
|
||||
DEBUG(dbgInt, "== Tick " << stats->totalTicks << " ==");
|
||||
|
||||
// check any pending interrupts are now ready to fire
|
||||
ChangeLevel(IntOn, IntOff); // first, turn off interrupts
|
||||
// (interrupt handlers run with
|
||||
// interrupts disabled)
|
||||
CheckIfDue(FALSE); // check for pending interrupts
|
||||
ChangeLevel(IntOff, IntOn); // re-enable interrupts
|
||||
if (yieldOnReturn) { // if the timer device handler asked
|
||||
// for a context switch, ok to do it now
|
||||
yieldOnReturn = FALSE;
|
||||
status = SystemMode; // yield is a kernel routine
|
||||
kernel->currentThread->Yield();
|
||||
status = oldStatus;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::YieldOnReturn
|
||||
// Called from within an interrupt handler, to cause a context switch
|
||||
// (for example, on a time slice) in the interrupted thread,
|
||||
// when the handler returns.
|
||||
//
|
||||
// We can't do the context switch here, because that would switch
|
||||
// out the interrupt handler, and we want to switch out the
|
||||
// interrupted thread.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Interrupt::YieldOnReturn()
|
||||
{
|
||||
ASSERT(inHandler == TRUE);
|
||||
yieldOnReturn = TRUE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::Idle
|
||||
// Routine called when there is nothing in the ready queue.
|
||||
//
|
||||
// Since something has to be running in order to put a thread
|
||||
// on the ready queue, the only thing to do is to advance
|
||||
// simulated time until the next scheduled hardware interrupt.
|
||||
//
|
||||
// If there are no pending interrupts, stop. There's nothing
|
||||
// more for us to do.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::Idle()
|
||||
{
|
||||
DEBUG(dbgInt, "Machine idling; checking for interrupts.");
|
||||
status = IdleMode;
|
||||
if (CheckIfDue(TRUE)) { // check for any pending interrupts
|
||||
status = SystemMode;
|
||||
return; // return in case there's now
|
||||
// a runnable thread
|
||||
}
|
||||
|
||||
// if there are no pending interrupts, and nothing is on the ready
|
||||
// queue, it is time to stop. If the console or the network is
|
||||
// operating, there are *always* pending interrupts, so this code
|
||||
// is not reached. Instead, the halt must be invoked by the user program.
|
||||
|
||||
DEBUG(dbgInt, "Machine idle. No interrupts to do.");
|
||||
cout << "No threads ready or runnable, and no pending interrupts.\n";
|
||||
cout << "Assuming the program completed.\n";
|
||||
Halt();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::Halt
|
||||
// Shut down Nachos cleanly, printing out performance statistics.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::Halt()
|
||||
{
|
||||
cout << "Machine halting!\n\n";
|
||||
cout << "This is halt\n";
|
||||
kernel->stats->Print();
|
||||
delete kernel; // Never returns.
|
||||
}
|
||||
|
||||
int
|
||||
Interrupt::CreateFile(char *filename)
|
||||
{
|
||||
return kernel->CreateFile(filename);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::Schedule
|
||||
// Arrange for the CPU to be interrupted when simulated time
|
||||
// reaches "now + when".
|
||||
//
|
||||
// Implementation: just put it on a sorted list.
|
||||
//
|
||||
// NOTE: the Nachos kernel should not call this routine directly.
|
||||
// Instead, it is only called by the hardware device simulators.
|
||||
//
|
||||
// "toCall" is the object to call when the interrupt occurs
|
||||
// "fromNow" is how far in the future (in simulated time) the
|
||||
// interrupt is to occur
|
||||
// "type" is the hardware device that generated the interrupt
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Interrupt::Schedule(CallBackObj *toCall, int fromNow, IntType type)
|
||||
{
|
||||
int when = kernel->stats->totalTicks + fromNow;
|
||||
PendingInterrupt *toOccur = new PendingInterrupt(toCall, when, type);
|
||||
|
||||
DEBUG(dbgInt, "Scheduling interrupt handler the " << intTypeNames[type] << " at time = " << when);
|
||||
ASSERT(fromNow > 0);
|
||||
|
||||
pending->Insert(toOccur);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Interrupt::CheckIfDue
|
||||
// Check if any interrupts are scheduled to occur, and if so,
|
||||
// fire them off.
|
||||
//
|
||||
// Returns:
|
||||
// TRUE, if we fired off any interrupt handlers
|
||||
// Params:
|
||||
// "advanceClock" -- if TRUE, there is nothing in the ready queue,
|
||||
// so we should simply advance the clock to when the next
|
||||
// pending interrupt would occur (if any).
|
||||
//----------------------------------------------------------------------
|
||||
bool
|
||||
Interrupt::CheckIfDue(bool advanceClock)
|
||||
{
|
||||
PendingInterrupt *next;
|
||||
Statistics *stats = kernel->stats;
|
||||
|
||||
ASSERT(level == IntOff); // interrupts need to be disabled,
|
||||
// to invoke an interrupt handler
|
||||
if (debug->IsEnabled(dbgInt)) {
|
||||
DumpState();
|
||||
}
|
||||
if (pending->IsEmpty()) { // no pending interrupts
|
||||
return FALSE;
|
||||
}
|
||||
next = pending->Front();
|
||||
|
||||
if (next->when > stats->totalTicks) {
|
||||
if (!advanceClock) { // not time yet
|
||||
return FALSE;
|
||||
}
|
||||
else { // advance the clock to next interrupt
|
||||
stats->idleTicks += (next->when - stats->totalTicks);
|
||||
stats->totalTicks = next->when;
|
||||
// UDelay(1000L); // rcgood - to stop nachos from spinning.
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(dbgInt, "Invoking interrupt handler for the ");
|
||||
DEBUG(dbgInt, intTypeNames[next->type] << " at time " << next->when);
|
||||
|
||||
if (kernel->machine != NULL) {
|
||||
kernel->machine->DelayedLoad(0, 0);
|
||||
}
|
||||
|
||||
inHandler = TRUE;
|
||||
do {
|
||||
next = pending->RemoveFront(); // pull interrupt off list
|
||||
next->callOnInterrupt->CallBack();// call the interrupt handler
|
||||
delete next;
|
||||
} while (!pending->IsEmpty()
|
||||
&& (pending->Front()->when <= stats->totalTicks));
|
||||
inHandler = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// PrintPending
|
||||
// Print information about an interrupt that is scheduled to occur.
|
||||
// When, where, why, etc.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
PrintPending (PendingInterrupt *pending)
|
||||
{
|
||||
cout << "Interrupt handler "<< intTypeNames[pending->type];
|
||||
cout << ", scheduled at " << pending->when;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// DumpState
|
||||
// Print the complete interrupt state - the status, and all interrupts
|
||||
// that are scheduled to occur in the future.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Interrupt::DumpState()
|
||||
{
|
||||
cout << "Time: " << kernel->stats->totalTicks;
|
||||
cout << ", interrupts " << intLevelNames[level] << "\n";
|
||||
cout << "Pending interrupts:\n";
|
||||
pending->Apply(PrintPending);
|
||||
cout << "\nEnd of pending interrupts\n";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user