NachOS/code/userprog/exception.cc

229 lines
6.8 KiB
C++

// exception.cc
// Entry point into the Nachos kernel from user programs.
// There are two kinds of things that can cause control to
// transfer back to here from user code:
//
// syscall -- The user code explicitly requests to call a procedure
// in the Nachos kernel. Right now, the only function we support is
// "Halt".
//
// exceptions -- The user code does something that the CPU can't handle.
// For instance, accessing memory that doesn't exist, arithmetic errors,
// etc.
//
// Interrupts (which can also cause control to transfer from user
// code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 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 "system.h"
#include "syscall.h"
//----------------------------------------------------------------------
// UpdatePC : Increments the Program Counter register in order to resume
// the user program immediately after the "syscall" instruction.
//----------------------------------------------------------------------
static void
UpdatePC ()
{
int pc = machine->ReadRegister (PCReg);
machine->WriteRegister (PrevPCReg, pc);
pc = machine->ReadRegister (NextPCReg);
machine->WriteRegister (PCReg, pc);
pc += 4;
machine->WriteRegister (NextPCReg, pc);
}
//----------------------------------------------------------------------
// ExceptionHandler
// Entry point into the Nachos kernel. Called when a user program
// is executing, and either does a syscall, or generates an addressing
// or arithmetic exception.
//
// For system calls, the following is the calling convention:
//
// system call code -- r2
// arg1 -- r4
// arg2 -- r5
// arg3 -- r6
// arg4 -- r7
//
// The result of the system call, if any, must be put back into r2.
//
// And don't forget to increment the pc before returning. (Or else you'll
// loop making the same system call forever!
//
// "which" is the kind of exception. The list of possible exceptions
// are in machine.h.
//----------------------------------------------------------------------
int copyStringFromMachine(int from, char *to, unsigned size)
{
unsigned i = 0;
int res;
// Need to read size-1 to put final /0 if needed
while((i<size-1) && (machine->ReadMem(from+i,1,&res))){
*(to+i) = (char)res;
if ((char)res == '\0'){
return i;
}
i++;
}
*(to+i)='\0';
return size;
}
int copyStringToMachine(int to, char* from, unsigned size)
{
unsigned i;
for (i=0; i < size - 1; i++) {
if (from[i] == '\0') {
break;
}
machine->WriteMem(to+i,1,(int)from[i]);
}
// Write the last /0
machine->WriteMem(to+i,1,'\0');
return i;
}
void
ExceptionHandler (ExceptionType which)
{
int type = machine->ReadRegister (2);
int address = machine->registers[BadVAddrReg];
switch (which)
{
case SyscallException:
{
switch (type)
{
case SC_Halt:
{
DEBUG ('s', "Shutdown, initiated by user program.\n");
interrupt->Powerdown ();
break;
}
#ifdef CHANGED
case SC_Exit:
{
int ret = machine->ReadRegister(4);
printf("Exit code %d\n", ret);
interrupt->Powerdown();
break;
}
case SC_PutChar:
{
DEBUG ('s', "PutChar.\n");
consoledriver->PutChar((char)machine->ReadRegister(4));
break;
}
case SC_PutString:
{
DEBUG ('s', "PutString.\n");
int readsize;
int addr_start = machine->ReadRegister(4);
do {
char* read = new char[MAX_STRING_SIZE];
readsize = copyStringFromMachine( addr_start, read, MAX_STRING_SIZE);
consoledriver->PutString(read);
// beware, the last element of our buffer was added by
// copyStringFromMachine(), we need to remove one memory case
// or the first char of our next read will be
// forgotten
addr_start += readsize - 1;
delete [] read;
} while(readsize == MAX_STRING_SIZE);
break;
}
case SC_GetChar:
{
DEBUG ('s', "GetChar.\n");
// Get the char input an write it to the register n.2
int c = consoledriver->GetChar();
machine->WriteRegister((2), c);
break;
}
case SC_GetString:
{
DEBUG ('s', "GetString.\n");
int writesize = 0;
int addr = machine->ReadRegister(4);
int size = machine->ReadRegister(5);
DEBUG ('s', "size:%d\n", size);
do {
char* write = new char[MAX_STRING_SIZE];
consoledriver->GetString(write, MAX_STRING_SIZE);
DEBUG('s', "write:%s:\n", write);
writesize = copyStringToMachine(addr, write, MAX_STRING_SIZE);
addr += writesize;
DEBUG('s', "Writed:%d\n", writesize);
delete [] write;
} while( writesize == MAX_STRING_SIZE - 1 );
break;
}
#endif
default:
{
printf("Unimplemented system call %d\n", type);
ASSERT(FALSE);
}
}
// Do not forget to increment the pc before returning!
UpdatePC ();
break;
}
case PageFaultException:
if (!address) {
printf("NULL dereference at PC %x!\n", machine->registers[PCReg]);
ASSERT (FALSE);
} else {
printf ("Page Fault at address %x at PC %x\n", address, machine->registers[PCReg]);
ASSERT (FALSE); // For now
}
break;
case ReadOnlyException:
printf ("Read-Only at address %x at PC %x\n", address, machine->registers[PCReg]);
ASSERT (FALSE); // For now
break;
case BusErrorException:
printf ("Invalid physical address at address %x at PC %x\n", address, machine->registers[PCReg]);
ASSERT (FALSE); // For now
break;
case AddressErrorException:
printf ("Invalid address %x at PC %x\n", address, machine->registers[PCReg]);
ASSERT (FALSE); // For now
break;
case OverflowException:
printf ("Overflow at PC %x\n", machine->registers[PCReg]);
ASSERT (FALSE); // For now
break;
case IllegalInstrException:
printf ("Illegal instruction at PC %x\n", machine->registers[PCReg]);
ASSERT (FALSE); // For now
break;
default:
printf ("Unexpected user mode exception %d %d %x at PC %x\n", which, type, address, machine->registers[PCReg]);
ASSERT (FALSE);
break;
}
}