5.4 System calls and exception handling

User programs invoke system calls by executing the MIPS syscall instruction, which causes the Nachos kernel exception handler to be invoked (with the cause register set to Processor.exceptionSyscall). The kernel must first tell the processor where the exception handler is by calling Machine.processor().setExceptionHandler().

The default Kernel exception handler, UserKernel.exceptionHandler(), reads the value of the processor’s cause register, determines the current process, and invokes handleException on the current process, passing the cause of the exception as an argument. Again, for a syscall, this value will be Processor.exceptionSyscall.

1   public void handleException(int cause) {
2   Processor processor = Machine.processor();
3  
4   switch (cause) {
5   case Processor.exceptionSyscall:
6   int result = handleSyscall(processor.readRegister(Processor.regV0),
7   processor.readRegister(Processor.regA0),
8   processor.readRegister(Processor.regA1),
9   processor.readRegister(Processor.regA2),
10   processor.readRegister(Processor.regA3));
11   processor.writeRegister(Processor.regV0, result);
12   processor.advancePC();
13   break;
14  
15   default:
16   Lib.debug(dbgProcess, "Unexpected exception: "
17   + Processor.exceptionNames[cause]);
18   Lib.assertNotReached("Unexpected exception");
19   }
20   }

The syscall instruction indicates a system call is requested, but doesn’t indicate which system call to perform. By convention, user programs place the value indicating the particular system call desired into MIPS register r2 (the first return register, v0) before executing the syscall instruction. Arguments to the system call, when necessary, are passed in MIPS registers r4 through r7 (i.e. the argument registers, a0 ... a3), following the standard C procedure call convention. Function return values, including system call return values, are expected to be in register r2 (v0) on return. Only the halt system call has been implemented, you will be asked to complete the implementation of the method UserProcess.handleSyscall for other system calls. Note that the registers do NOT store the values of the arguments, rather, the virtual memory locations of these arguments. For example, consider a method that handles open system call. From test/syscall.h, we have

1   int open(char ⋆name);

Thus, there should only be one argument to the method, say, handleOpen(int name). To get the actual string that stores the file name to be opened, one can use the method UserProcess.readVirtualMemoryString(name, maxFileNameLength), where maxFileNameLength is the (programmer defined maximum length of file names).

Implementation Note: When accessing user memory from within the exception handler (or within Nachos in general), user-level addresses cannot be referenced directly. Recall that user-level processes execute in their own private address spaces, which the kernel cannot reference directly. Use readVirtualMemory(), readVirtualMemoryString(), and writeVirtualMemory() to make use of pointer arguments to syscalls.