// 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) && (machine->ReadMem(from+i,1,&res))){ DEBUG('s', "\n ->copyFromMachine keycode:%d", 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 ; i++) { DEBUG('s', "\n ->copyToMachine keycode:%d", (int)from[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 + 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 an 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) int n_char = ( size < MAX_STRING_SIZE) ? size : MAX_STRING_SIZE; // allocate on byte more than buffer to put our \0 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 = consoledriver->GetInt(); DEBUG('s', "Number entered: %d\n", n); machine->WriteRegister(2,n); 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; } }