Discussion Section: Wednesday, March 24, 1999
Getting Started with Lab 3


GENERAL TIPS:

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 nacho 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 will may have more.

Get started SOON, or better yet, NOW!  Too many people waited too long before they started lab 2.  Do not make that mistake here. There will be NO extensions and late penalties will apply.
 

You need to Implement some of the System Calls

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


Getting Started with the Syscall Implementations...


Tips for Create()


Tips for Close()

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


Tips for Read()


Tips for Open()

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


Tips for Write()


Tips for Length()

I think you can figure this one out by yourselves.
 

Tips for SyscallHalt()


Tips for SyscallExit()


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.
 

void ExceptionHandler(ExceptionType which)

The system call exceptions are caught by ExceptionHandler which calls the proper syscall handlers

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


Global variables in threads/system.h(.cc)


Console


Sample TestCases

Write VERY simple test codes to generate some of the exceptions: divide by zero (watch out, compiler tries to optimize constants), over/underflow, etc.

/* test_DivideByZero.c such that compiler does not detect the error */
int main() {
    int a;
    int b;
    int c;

    a = 7;
    c = a*a;
    b = (a*a)/(a*a - c);

  /* not reached */
}

ISSUE:   Nachos does not support printf and many other standard C library functions in user programs (e.g., test/halt.c, test/test_DivideByZero.c).  The functions that are supported are in machine/sysdep.h(.cc).  This means you won't have the full arsenal of
C routines to debug your user programs.  On the other hand, the programs should be very simple.

Additional info about running user programs can be found.


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++];
  }
}