Discussion Section: Wednesday, November 10th, 1999
Getting Started with Lab 3


GENERAL TIPS:

For each system call, you will need to sanity check the parameters, list all the things that can possibly go wrong (never assume that your program will be well-behaved; we will
deliberately test your implementations against malicious user programs) and design a way to take evasive action. A good chunk of real OS code does this sort of bullet-proofing.

Put checks everywhere! Just because a Syscall routine gets a file ID doesn't mean the file exists. Also use ASSERTs to verify that what you think must be true is. Both of these techniques help you narrow where problems exist.

Test your code as you go along. Make up your own test cases for debug. Put debug print statements in the nachos code (exception.cc mostly) to see intermediate values because the user code is highly restricted in what it can use for IO (which is basically the console, but you can't use the console for debug until you've debugged the code).

Code for any of these routines should not exceed 25 or so lines (except for ExceptionHandler()) and many routines can be done in a dozen lines.  If you are greatly exceeding this metric then you are probably doing something wrong.  However, if you put a lot of checks and printouts then you  may have more.

This assignment is rather long. If I were you I would start NOW (i.e if I hadn't already started last week) !

The ExceptionHandler
All exceptions are caught by ExceptionHandler (defined in exception.cc) which calls the proper syscall handlers

For more info on this read:
Salsa: System Calls and Exception Handling
Roadmap: System Calls and ExceptionHandling

NOTE:  Break your syscalls into functions .  One long ExceptionHandler function is unacceptable coding style. ExceptionHandler should call SyscallExit, SyscallJoin, and SyscallExec as well as all of the other Syscall Functions.  You WILL loose points if you implement one large ExceptionHandler function . Read the values out of machine->readRegister(4), machine->readRegister(5), machine->readRegister(6) and send them as parameters to the functions.
Your exception handler should look something like this :

void
ExceptionHandler(ExceptionType which)
{
        int type, arg1, arg2,......;    // Figure out a way of finding the type of syscall exception, arguments, etc.
 
        switch(which)
        {
         case SyscallException:
                  switch(type)
                  {
                  case SC_Create:
                        SyscallCreate( arguments, etc) ;
                        break ;
                  case SC_Open:
                  .
                  .
                   }
         case PageFaultException:
            .
            .
        }
}

PASSING ARGUMENTS TO SYSCALLS IS TRICKY!

Values are passed to the ExceptionHandler via registers as integers. You must cast the register values to the appropriate types in your SyscallXXX invocations.

RETURNING A VALUE IS TRICKY!

The kernel is communicating with the user process via machine registers. See the top comments in exception.cc that states values are returned in reg 2. Read up in machine.cc on ReadRegister and WriteRegister. Register read and write examples are in the shell code of exception.cc.

You need to Implement some of the System Calls

    1. SyscallCreate
    2. SyscallOpen
    3. SyscallClose
    4. SyscallRead
    5. SyscallWrite


Getting Started with the Syscall Implementations...


Tips for Create()

Tips for Open()

opens a file if it exists, or, if it doesn't, creates it and then opens it


Tips for Close()

close() finds a file by ID and removes it from the list of files

Tips for Write()     Console Tips for Read()


Tips for SyscallExec()

            AddrSpace *aspace = new AddrSpace(...pass appropriate arguments here...)         //----------------------------------------------------------------------
        // ExecProcess
        //      This is called by SyscallExec to actually run the new process.
        //      It is a forked kernel thread which begins by setting up it's
        //      address space and initializes things.  It then calls machine->Run
        //      which causes it to execute.
        //
        //----------------------------------------------------------------------
 

        void ExecProcess(int execFileName)
        {
          DEBUG('4', "Starting process %s\n", (char *) execFileName);
          currentThread->space->InitRegisters();
          currentThread->space->RestoreState(); // load page table register
          machine->Run();                                    // jump to the user progam
          ASSERT(FALSE);                                // machine->Run never returns;
                                                                        // the address space exits
                                                                        // by doing the syscall "exit"
        }

Tips for SyscallJoin()


Tips for SyscallExit()

          1.this function releases the address space of the process and joins (calling SyscallJoin) on any children of the process that were never joined on (thus freeing up
            'zombies')
          2.all open files are closed (as before)
          3.function then puts the exit status in processPtr->status
          4.semaphore is used to prevent the parent from joining before this function is called
          5.then the thread dies gracefully using currentThread->Finish()


Errata in the Given Code

Use the following code for UserToKernel and KernelToUser. The distributed code uses ReadMem/WriteMem, but they appear to have problems. The other method (also given in the distribution but commented out) works. Comment/Uncomment appropriately.

//-----------------------------------------------------------
// UserToKernel
//      Copies a buffer of length from user virtual address to a kernel
//      structure.
//-----------------------------------------------------------

char *UserToKernel(char *source, int length)
{
  int current=0;
  int phyAddr;
  char *dest;

  if(!source)
    return (int)0;
  dest=new char[length];
  while(current<length){
    phyAddr=GetPhysAddrInKernel((int)source++, FALSE);
    if(!phyAddr)
      return 0;
    dest[current++]=machine->mainMemory[phyAddr];
  }
  return dest;
}

//-------------------------------------------------------
// KernelToUser
//      Copies a buffer of length from kernel space to user space
//-------------------------------------------------------

void KernelToUser(char *dest, char *source, int length)
{
  int current=0;
  int phyAddr;

  while(current<length){
    phyAddr=GetPhysAddrInKernel((int)dest++, TRUE);
    // dest won't get deleted if address is invalid
    machine->mainMemory[phyAddr]=source[current++];
  }
}