Note that the answers in this file were originally written as reference material for grading, so they're more complete than you needed to be to get full credit. ======================================================================== 1. In section 3.1 of the text, you are introduced to the mechanisms used to transfer control between user processes and the operating system. Tell us where we can find the first line of OS/161 code that is executed when a trap occurs. Then tell us where control gets transferred to from that point. What about an interrupt? How does that differ? kern/arch/mips/locore/exception-mips1.S contains trap-handling code. If it's a UTLB exception, the first line of code is 69. If it is a general exception, the first line is 87. Both are a simple "j common_exception". common_exception creates a trapframe and passes it to mips_trap in kern/arch/mips/locore/trap.c. From there: Syscalls will be directed to the syscall function in kern/arch/mips/syscall/syscall.c. VM faults will be directed to vm_fault, which for now is in kern/arch/mips/vm/dumbvm.c. Interrupts will be directed to mainbus_interrupt in kern/arch/sys161/dev/lamebus_machdep.c. Interprocessor interrupts are then directed to interprocessor_interrupt in kern/thread/thread.c. Timer interrupts are directed to hardclock in kern/thread/clock.c. General interrupts are passed to lamebus_interrupt in kern/dev/lamebus/lamebus.c Notice that, after mainbus_interrupt, the interrupt is handled outside of the arch directory Other errors, such as exceptions caused by invalid TLB entries, result in a panic. Interrupts don't differ, as on MIPS they're a special case of trap. Or they differ in that they're dispatched differently in mips_trap. (Either of these answers is fine.) ======================================================================== 2. A system call, such as write, ultimately leads to a trap. Find where in OS/161 this trap is invoked. The "syscall" instruction on line 84 of userland/lib/libc/arch/mips/syscalls-mips.S. Realize that this is in userland! ======================================================================== 3. Why do you suppose there are libc functions in the "common" part of the source tree (common/libc) as well as in userland/lib/libc? These are basic C functions that are used not only in libc but also in the kernel. ======================================================================== 4. Name two things that configure configures. What might invalidate that configuration and make you need/want to rerun it? The complete list: - target platform (PLATFORM) - target machine (MACHINE) - location of the installed OS/161 tree (OSTREE) - debug vs. optimize for userland (DEBUG) - whether the host OS has (goes into COMPAT_CFLAGS, COMPAT_TARGETS) - whether the host OS has ntohll/htonll (goes into COMPAT_CFLAGS, HOST_CFLAGS) Note that while sys161 and mips are the hardwired values of PLATFORM and MACHINE, and no other values are actually accepted, this is where they're set. Reasons to rerun: PLATFORM/MACHINE: wanting to port to different hardware. (saying "wanting to run on different hardware" is ok, they may not really understand porting yet at this stage and that seems fine to me) OSTREE/DEBUG: wanting to use a different value COMPAT_*: switching to a different crosscompile host OS ======================================================================== 5. What are some of the details which would make a function "machine dependent"? Why might it be important to maintain this separation, instead of just putting all of the code in one function? Any function written in assembly is machine-dependent, since MIPS assembly won't play nicely on an x86. The registers and instruction sets are completely different. Any code that relies on specific registers (such as your trapframe code) or hardware functionality (such as installing exception handlers at specific addresses, or mapping device registers at particular addresses) is going to be machine-dependent. It is better to separate machine-dependent code from machine- independent than to mash it all together because organization is vital to maintainability. If you're going to port to another machine, you want to be able to easily find what machine-dependent code you need to write, and even more importantly, understand what that code is supposed to do because it has a clearly defined external interface. ======================================================================== 6. How large is a trapframe? Why? Inside of kern/arch/mips/include/trapframe.h, we count 37 uint32_ts. Each one is 4 bytes, for a total of 148 bytes. It is this size because it must store exactly 37 registers, including the general purpose registers, the cause register, EPC, etc. ======================================================================== 7. Under what circumstances should you re-run the kern/conf/config script? You added or removed a device in your kernel config, or you changed kernel configuration options. Also, if you added new source files to the kernel. (Or removed them, but that's less common.) ======================================================================== 8. Under what circumstances should you run bmake depend in kern/compile/ASST? If you just configured ASST, or if you added or removed any #include directives in any of your source files. ======================================================================== 9. Under what circumstances should you run bmake or bmake install in kern/compile/ASST? If you changed any of the kernel code. ======================================================================== 10. What is the point of all the preprocessor hackery in array.h? What does it accomplish (vs. just having the plain "struct array") and when might you use it? The point is to be able to generate type-safe arrays, so the compiler will object if you accidentally use the wrong array or put the wrong objects in an array. You'd use it anytime you're making an array, really, except perhaps for simple well-isolated small cases. ======================================================================== 11. When you booted your kernel, you found that there were several commands that you could issue to experiment with it. Explain exactly where and what you would have to do to add a command that printed out, "Hello world!" Inside of kern/startup/menu.c, we can add a function like: static int cmd_helloworld(int nargs, char **args) { (void)nargs; (void)args; kprintf("Hello world!\n"; return 0; } Then, to the opsmenu array, we add something like "[hello] print Hello world", and to cmdtable add an entry {"hello", cmd_helloworld}. ======================================================================== 12. Why do we need to include these in your OS/161 distribution? Why can't you just use the standard utilities that are present on the machine on which you're working? The host system's standard utilities are not compiled for the correct architecture (MIPS) or operating system ABI (OS/161). Even if they were, they may use system calls that you won't be implementing. ======================================================================== 13. When a user program exits, what is done with the program's return value? When main returns, the value is stored in the v0 register. It is then put inside of the s0 register in case things go wrong with the exit system call. It is stored in the a0 register to act as the argument for the exit system call. ======================================================================== 14. Imagine that you wanted to add a new system call. List all the places that you would need to modify/add code. Then review your answers to questions 7-9 and note which of those actions you need to take in order to test the new system call. - Add a new entry with a unique number to kern/include/kern/syscall.h. - Add a case to kern/arch/mips/syscall/syscall.c:syscall() to dispatch the call to MI code. - Add a prototype for the MI code to kern/include/syscall.h. - Implement the MI code for system call, probably in a file in kern/syscall. - You will also want to add a prototype in the userlevel includes somewhere. If this added a new source file, rerun kern/conf/config. Then do "bmake depend", "bmake", and "bmake install" in kern/compile/ASST#. ======================================================================== 15. Refer to the document Using GDB and run gdb on your kernel. Experiment a bit and follow the execution from the start.S file through the main menu kmain and then to the code that executes some of the commands. Explain the control flow from start.S through the menu and on to other parts of the kernel. start.S sets up the exception handlers at the appropriate addresses, flushes the instruction cache, initializes the TLB, and goes to kmain. kmain calls boot, which does initialization for RAM, threads, the hardclock, the virtual file system, the mainbus, pseudo-devices, VM, kprintf, and CPUs. Then, kmain calls menu, which will call menu_execute to parse any commands passed to the kernel at the command-line and call cmd_dispatch to run any of those commands. When we return to menu, we fall into a loop, accepting user input for new commands and executing them.