Due: Electronically, by 11:59pm, Jan. 26 (Friday), 2007.
Mini Milestone: due electronically by 11:59pm, Jan. 19, 2007.
This assignment is to be done individually.
GOAL: To become familiar with OS/161 and understand the user/kernel interface. To develop and use debugging tools that will come in handy for the rest of the term.
This assignment will familiarize you with OS/161, the operating system with which you will be working this semester, and System/161, the machine simulator on which OS/161 runs. Most importantly, you will implement 3 simple system calls that will help you understand the user/kernel interface and you will add a kernel menu option that should help you debug future assignments.
The first part of this document briefly discusses the code on which you will be working and the tools you will be using. You can find more detailed information on CVS and GDB in separate handouts. The following sections provide precise instructions on exactly what you must do for the assignment. Each section with (hand me in) at the beginning indicates a section where there is something that you must do for the assignment.
Before beginning the assignment, make sure you have read the Getting Started with OS/161 document.
After working through the Getting Started doc, you should have obtained, built, and run your own kernel. Among the output that is printed as the kernel boots, you should notice the string "Put-your-group-name-here's system version 0". Your first task is to find the kprintf statement that generates that line, and modify it to print your CDF login id. For example,
c4demkeb - Angela Brown's system version 0 (ASST0 #1)
Format requirements The first word on the line must be your CDF login id, followed by " - ". The characters between the " - " and the "'s" should be a string by which you want to identify yourself. It is customary to use your name, but feel free to get creative (Note however, that we'll be printing this on the course web site!).
Submission of Mini Milestone: Send email from your cdf account to demke@cdf, with subject "os161 - main.c" and your modified main.c attached. We will automatically build a kernel with your main.c file, run it, and verify that your CDF login id appears in the kernel version string. You will receive an automated email back with the results of the build and run. A list of submissions that have passed the check can be found at http://www.cdf.toronto.edu/~csc369h/winter/a0_minicheck.html.
You should use the code reading questions included below to help guide you through reviewing the existing code. While you needn't review every line of code in the system in order to answer all the questions, you should be reading to understand the code, not just looking for answers to the specific questions.
These questions are not meant to be tricky -- most of the answers can be found in comments in the OS/161 source, though you may have to look elsewhere (such as the textbook) for some background information. Place the answers to the following questions in a file called ~/csc369/asst0/code-reading.txt. Make sure the answers are clearly labeled, and written in proper English.
The Kern Subdirectory
Look at the files in kern/arch/mips/include/ to answer these questions. These are include files for the machine-specific constants and functions.
Question 1. Which register number is used for the stack pointer (sp) in OS/161?
Question 2. What bus/busses does OS/161 support?
Question 3. What is the difference between splhigh and spl0?
Question 4. Why do we use typedefs like int32_t instead of simply saying "int"?
Look at the files in kern/arch/mips/mips/ to answer these questions. These are the source files containing the machine-dependent code that the kernel needs to run. Most of this code is quite low-level.
Question 5. What does splx return?
Question 6. What is the highest interrupt level?
Look at the files in kern/include/ to answer these questions. These are the include files that the kernel needs. The kern subdirectory contains include files that are visible not only to the operating system itself, but also to user-level programs. (Think about why it's named "kern" and where the files end up when installed.)
Question 7. How frequently are hardclock interrupts generated?
Question 8. What function causes a thread to exit?
Question 9. How large are OS/161 pids?
Question 10. What is the maximum length of a filename in OS/161?
Question 11. What is the system call number for a getpid?
Question 12. What error should be generated if the kernel cannot allocate memory?
Question 13. What is the purpose of sys_errlist?
Look at the files in kern/main/ and kern/thread/ to answer these questions. The main/ subdirectory is where the kernel is initialized and where the kernel main function is implemented. The thread/ subdirectory manages thread, which are the fundamental abstraction on which the kernel is built.
Question 14 Is it OK to initialize the scheduler before the virtual memory (vm) system? Why (not)?
Question 15. What is a zombie?
Question 16. What function is called to handle the "p" menu command?
A small number of additional questions relating to the system call interface appear later in this document. Make sure you include them in your answers.
In this part of the assignment, you will implement a simplified version of one existing system call (_exit) and add 2 new system calls to OS/161. You will also write 2 user-level programs to test your new system calls.
You should surround all code that you write or modify for this assignment with:
/* BEGIN c4demkeb's ASST0 SOLN */
/* END c4demkeb's ASST0 SOLN */
to make it easy to identify your code (use your own CDF login id, not c4demkeb, of course!).
The system calls you will implement are:
All user programs end by calling the "_exit" system call, even if your main() function does not end with the C library exit() function. [Ever wonder who calls main() in the first place, or where it goes when it returns? Have a look at src/lib/crt0/mips-crt0.S - this is where your user-level C programs really start executing... you should be able to find the call to main() and the call to _exit after main returns.] Without an implementation of _exit, the threads created to handle user programs just hang around forever, executing an infinite loop in user space and taking up a lot of the CPU time. [You should be able to locate that loop in mips-crt0.S as well.]
You should handle the _exit system call in such a way that the thread calling _exit is fully destroyed and all memory returned to the system. HINT: You should find an existing function that is almost an ideal handler for the _exit system call -- modify this function to receive the exit code parameter (you will also have to modify all other uses of this function to pass an exit code). Use a DEBUG() statement in this function to print out the exit code if the DB_SYSCALL messages are enabled.
This system call should be added as system call number 40. It is handled by having the kernel print "Hello World" and a newline using the internal kprintf() function. It takes no arguments but returns the return value from kprintf to the user level. You will need to understand how to add a new system call number, and build the user and kernel sides of the interface. Study the existing reboot() system call to get started.
This system call should be added as system call number 41. It passes a single character to the operating system, and is handled by having the kernel print that character using the internal kprintf() function. The return value should be 1 if the character is printed, and an error otherwise.
Note that the system call signatures given here are the user-level functions. In other words, there are the functions that user programs will call to invoke these system calls. You will likely choose to use different names for the handlers of these functions in the kernel (e.g., sys_reboot is called to handle the reboot system call).
You will need to modify the code in kern/arch/mips/mips/syscall.c to detect your new system calls and dispatch appropriate system call handlers. Although these system calls are simple enough to implement fully within syscall.c, it is good programming practice to put the handlers in a separate function in a file in the kern/userprog subdirectory. You should name this file simple_syscalls.c. You will also need to add an entry for this new file in the kern/conf/conf.kern file, and reconfigure your kernel so that it is included in the build. Also, because you want to call functions from syscall.c that are defined elsewhere, you should add prototypes for these functions to the kern/include/syscall.h header file, similar to what is done for the sys_reboot function.
Please read the document Understanding System Calls for additional information about how user-level programs are started from the OS/161 kernel, and how system calls operate on both the system and user side of the interface. After reading this document and the files it refers to, you should be able to answer the following questions (include the answers in your code_reading.txt file).
Question 17. What (briefly) is the purpose of userptr_t?
Question 18. What is the numerical value of the exception code for a MIPS system call?
Question 19. How many bytes is an instruction in MIPS? (Answer this by reading mips_syscall() carefully, not by looking somewhere else.)
Question 20. How can you test that helloworld has actually returned the correct value to the user level?
Testing and using your system calls
To test your system calls:
Modify the kern/main/menu.c file to add a new menu option to modify the dbflags variable, allowing you to turn debugging flags on or off from the OS/161 menu.
For this part, you will want to brush up on the C bit-wise operators, "|" (bitwise OR), "&" (bitwise AND), "~" (bitwise NOT), "<<" (shift left), and ">>" (shift right).
Begin by examining the DEBUG macro in kern/include/lib.h. Look for places where DEBUG macros are used in the current OS/161 code. Note that the DEBUG macro allows you to conditionally print certain messages to the console, depending on the value of the dbflags variable. Each category of print messages has a separate "bit flag" to determine if it should be printed or not. For example, DEBUG messages related to system calls are only printed if the bit for DB_SYSCALL is set to 1 in the dbflags variable. Each of the DB_FOO values has only a single bit set to 1 (try writing some of them out as binary numbers if this isn't clear), so you can combine them to enable various combinations of DEBUG statements. To get DEBUG messages for threads and system calls, for example, you can set dbflags to DB_SYSCALL | DB_THREADS. All flags can be enabled by setting dbflags to 0xffffffff ("all bits set to 1"), and all flags can be disabled by setting dbflags to 0.
Currently, there are two ways to set the dbflags variable. One way is to add statements in the OS/161 source code (for instance, you could add the line "dbflags = DB_SYSCALL | DB_THREADS;" to the boot() function in main.c. This can be fairly inflexible, however, requiring you to edit the source code and recompile your kernel to change the debugging output. The second way is to use the cs161-gdb debugger. For instance, you can type:
(gdb) set variable dbflags = 0x2 | 0x10
to turn on DB_SYSCALL and DB_THREADS debugging statements at any point. The problem with this strategy should be obvious - the #define statements in lib.h are just C pre-processor macros, not variables, so the debugger doesn't know what DB_SYSCALL means. Instead, you have to look up the values in lib.h and enter them by hand, which can be annoying and error-prone.
Your mission for this assignment is to add an option to the OS/161 menu that will allow you to set and clear bits in the dbflags variable using meaningful strings for the various flags. The command should be "dbflags", the first argument should be either "+" if flags are being turned on, or "-" if flags are being turned off. If there are no additional arguments, the "+" or "-" will apply to all flags (that is, all on, or all off). Additional arguments should be a list of flags to set. For example,
OS/161 kernel [? for menu]: dbflags + DB_SYSCALL DB_THREADS
should turn on the DB_SYSCALL and DB_THREADS bits in the dbflags variable, while leaving all other bits unchanged. Similarly,
OS/161 kernel [? for menu]: dbflags - DB_THREADS
should turn off the DB_THREADS flag, while leaving the other flags unchanged.
You should also provide a means to print the current value of the dbflags variable, in human-readable form (e.g. printing "DB_SYSCALL | DB_THREADS" as opposed to printing "0x12" or worse, printing "18"), which are the only options you get from the debugger.
Most, if not all, of your code for this part should go in the kern/main/menu.c file. Consider however what needs to be done to keep your new menu command up-to-date if new DEBUG flags are defined.
Tips
Using DEBUG
Write at least 5 DEBUG statements, using the DB_SYSCALL flag, in the code
you have written to handle system calls. Verify that you can control the
display of these messages when running your system call test programs using
the dbflags menu option you have written.
Complete the Group Signup form. Everyone must complete the form individually to receive the marks on this assignment. You are strongly encouraged to find your own groups, but if you cannot, the form includes an option to ask for help in forming a group. If you do not fill out the form by the deadline, you can still find your own group, but I will not form a group for you.
Your assignment will be evaluated using the following criteria:
Please submit a single compressed tar file of your entire src andasst0 directories (the asst0 directory should contain your code-reading.txt file and your design.txt file). Make sure you do a "make clean" in the src directory, and remove the generated kern/compile/ASST0 directory first. For example:
skywolf% cd ~/csc369/src
skywolf% make clean
skywolf% cd kern/compile
skywolf% rm -rf ASST0
skywolf% cd ~/csc369
skywolf% tar -czf asst0.tgz asst0 src
skywolf% submit -c csc369h -a A0 asst0.tgz
You can check your submission using "submit -l -c csc369h -a A0", and resubmit if necessary using "submit -c csc369h -a A0 -f asst0.tgz".