280 lines
8.6 KiB
C++
280 lines
8.6 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"
|
|
|
|
#ifdef CHANGED
|
|
#include "userthread.h"
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
// 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` value into memory to put final /0 if needed
|
|
for(i=0; i < size; i++){
|
|
|
|
// What should we do if a problem occurs when accessing
|
|
// memory?
|
|
if (machine->ReadMem(from+i,1,&res) == false){
|
|
DEBUG('s', "Error When reading memory in copyStringFromMachine\n");
|
|
return -1;
|
|
}
|
|
DEBUG('s', "\n ->copyFromMachine keycode:%d", res);
|
|
*(to+i) = (char)res;
|
|
if ((char)res == '\0'){
|
|
return i;
|
|
}
|
|
}
|
|
*(to+size)='\0';
|
|
return i;
|
|
}
|
|
|
|
int copyStringToMachine(int to, char* from, unsigned size)
|
|
{
|
|
unsigned i;
|
|
for (i=0; i < size ; i++) {
|
|
DEBUG('s', "\n ->copyToMachine keycode:%d", (int)from[i]);
|
|
if (from[i] == '\0') {
|
|
break;
|
|
}
|
|
if ( machine->WriteMem(to+i,1,(int)from[i]) == false) {
|
|
DEBUG('s', "Error when writing memory in copyStringFromMachine\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// 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 + 1];
|
|
readsize = copyStringFromMachine( addr_start, read, MAX_STRING_SIZE);
|
|
DEBUG('s', "\nRead buffer:%s, size:%d\n", read, readsize);
|
|
consoledriver->PutString(read);
|
|
addr_start += readsize ;
|
|
delete [] read;
|
|
} while(readsize == MAX_STRING_SIZE);
|
|
break;
|
|
}
|
|
case SC_GetChar:
|
|
{
|
|
DEBUG ('s', "GetChar.\n");
|
|
// Get the char input and write it to the register n.2
|
|
int c = consoledriver->GetChar();
|
|
machine->WriteRegister(2, c);
|
|
break;
|
|
}
|
|
|
|
case SC_GetString:
|
|
{
|
|
DEBUG('s', "Begin GetString.\n");
|
|
int writesize = 0;
|
|
int addr = machine->ReadRegister(4);
|
|
int size = machine->ReadRegister(5);
|
|
DEBUG('s', "size:%d\n", size);
|
|
do {
|
|
// Do not process char before size
|
|
// but for now we don't have any method to determine if
|
|
// string passed to our function est bigger than expected (size)
|
|
// It seems to be easier to check than in userspace mode
|
|
int n_char = ( size < MAX_STRING_SIZE) ? size : MAX_STRING_SIZE;
|
|
|
|
// allocate on byte more than buffer to put our \0 at the end,
|
|
// if needed
|
|
char *write = new char[n_char + 1];
|
|
consoledriver->GetString(write, n_char);
|
|
writesize = copyStringToMachine(addr, write, n_char);
|
|
addr += writesize;
|
|
size = size - writesize;
|
|
DEBUG('s', "writed:%s, size:%d, remain:%d\n", write, writesize, size);
|
|
delete [] write;
|
|
} while( size > 0 && writesize == MAX_STRING_SIZE );
|
|
DEBUG('s', "End GetString\n");
|
|
break;
|
|
}
|
|
case SC_PutInt:
|
|
{
|
|
DEBUG('s', "PutInt.\n");
|
|
int n = machine->ReadRegister(4);
|
|
DEBUG('s', "PutInt parameter: %d\n", n);
|
|
consoledriver->PutInt(n);
|
|
}
|
|
break;
|
|
case SC_GetInt:
|
|
{
|
|
DEBUG('s', "GetInt\n");
|
|
int n;
|
|
int addr = machine->ReadRegister(4);
|
|
consoledriver->GetInt(&n);
|
|
DEBUG('s', "Number entered: %d\n", n);
|
|
machine->WriteMem(addr,sizeof(n),n);
|
|
break;
|
|
}
|
|
case SC_ThreadCreate:
|
|
{
|
|
int f = machine->ReadRegister(4);
|
|
int args = machine->ReadRegister(5);
|
|
do_ThreadCreate(f, args);
|
|
break;
|
|
}
|
|
case SC_ThreadExit:
|
|
{
|
|
do_ThreadExit();
|
|
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;
|
|
}
|
|
}
|