Instructor: Ding Yuan
Course Number: ECE344

Home
Discussion (Piazza)
Lab Documentation
Lab Assignments
Schedule and Lecture Notes
Grades (Quercus)

Operating Systems

ECE344, Winter 2024
University of Toronto


Assignment 1: An Introduction to OS161

Due date (hard deadline, no extension): Jan. 30, 11:59 pm.
Note: the group setup on UG machines won't be completed before Jan. 19. So any Git operations should be done only after that date.

Objectives

After this assignment, you should:

  • Be familiar with Git and GDB (the GNU Debugger).
  • Understand the source code structure of OS161, the software system we will be using this term.
  • Understand how System/161 emulates the MIPS hardware environment on which OS161 runs.
  • Understand the source code structure of System/161.
  • Be comfortable reading the OS161 source code and figuring out where things are done and how they are done.
  • Write some initial testing code to familiarize yourself with making changes to the OS161 environment.
  • Understand how to implement simple system calls.

Introduction

In this assignment, we will introduce:

System/161
The machine simulator for which you are building an operating system this term.
OS161
The operating system you will be designing, extending, and running this term.
Git
Git is a source code revision control system. It manages the source files of a software package so that multiple programmers may work simultaneously. Each programmer has a private copy of the source tree and makes modifications independently. The main copy of the source tree is stored in an area called the remote repository. The private copy of the code is called the working copy. Each programmer makes modifications to their working copy and then commits their modifications to the remote repository. Git attempts to intelligently merge multiple people's modifications, highlighting potential conflicts when it fails.
GDB (Gnu Debugger)
GDB allows you to examine what is happening inside a program while it is running. It lets you execute programs in a controlled manner and view and set the values of variables. In the case of OS161, it allows you to debug the operating system you are building instead of the machine simulator on which that operating system is running.

The first part of this document briefly discusses the code on which you'll be working and the tools you'll be using. You can find more detailed information on Git and GDB. The following sections provide instructions on what you must do for this assignment.

What are OS161 and System/161?

The code is divided into two main parts:

  • OS161: the operating system that you will augment in subsequent homework assignments.
  • System/161: the machine simulator that emulates the physical hardware on which your operating system will run. This course is about writing operating systems, not designing or simulating hardware. Therefore, you may not change the machine simulator.

The OS161 distribution contains a barebones operating system source tree, including some utility programs and libraries. After you build the operating system you boot, run, and test it on the simulator.

We use a simulator in OS161 because debugging and testing an operating system on real hardware is extremely difficult. The System/161 machine simulator has been found to be an excellent platform for rapid development of operating system code, while still retaining a high degree of realism. Apart from floating point support and certain issues relating to RAM cache management, it provides an accurate emulation of a MIPS processor.

Besides this initial assignment, there will be an OS161 programming assignment for each of the following topics:

  • ASST2 : Synchronization and concurrent programming
  • ASST3 : System calls and multiprogramming
  • ASST4 : Virtual memory

OS161 assignments are cumulative. You will need to build each assignment on top of your previous submission. So you will have to make sure that each of your assignments work correctly!

About Git

Most programming you have probably done has been in the form of 'one-off' assignments: you get an assignment, you complete it yourself, you turn it in, you get a grade, and then you never look at it again.

The commercial software world uses a very different paradigm: development continues on the same code base producing releases at regular intervals. This kind of development normally requires multiple people working simultaneously within the same code base, and necessitates a system for tracking and merging changes. Your will be working in teams of 2 on OS161 and will be developing a code base that will change over the couse of several assignments. Therefore, it is imperative that you start becoming comfortable with Git, an open source version control system.

Git is a powerful tool, but for OS161 you only need to know a subset of its functionality. The Git handout contains all the information you need to know and should serve as a reference throughout the term. If you'd like to learn more, there is comprehensive documentation available here.

About GDB

In some ways debugging a kernel is no different from debugging an ordinary program. On real hardware, however, a kernel crash will crash the whole machine, necessitating a time-consuming reboot. The use of a machine simulator such as System/161 provides several debugging benefits. First, a kernel crash will only crash the simulator, which only takes a few keystrokes to restart. Second, the simulator can sometimes provide useful information about what the kernel did to cause the crash, information that may or may not be easily available when running directly on top of real hardware.

To debug OS161 you must use a version of GDB configured to understand OS161 and MIPS. This is called cs161-gdb. This version of GDB has been patched to be able to communicate with your kernel through System/161.

An important difference between debugging a regular program and debugging an OS161 kernel is that you need to make sure that you are debugging the operating system, not the machine simulator. Type:

  % cs161-gdb sys161

and you are debugging the simulator. Not good. The handout Debugging with GDB provides detailed instructions on how to debug your operating system and a brief introduction to GDB.

Setting up your account

Login to the lab machines. On your first log-in, you should change the password on your account for security purposes. You can use the passwd command to do so.

Start by setting up your personal information for Git, if you have not done it previously. The "--global" option will update the .gitconfig file in your home directory.

cd
git config --global user.name "Your Name"
git config --global user.email you@example.com

Next step, make sure that your home directory is not accessible to others. This way you can ensure that you code will not be available to others accidentally.

cd
chmod 700 .  

Here, the permission 700 grants yourself full access to the home directory, and denies any access (no read, write or execute access) to group members or other users. For more information on Linux file permissions, you can read this article.

The OS161 tools are accessible from the workstation lab machines (ug*.eecg.utoronto.ca). The ranges of machines that are accessible are roughly ug51-ug100, ug132-ug180 and ug201-250. You will need to setup your path to access the OS161 tools. To do so, you should check whether you use csh or bash as follows:

  % echo $0

If the output shows sh or bash, then you are using bash. If the output shows csh or tcsh, then you are using csh.

  1. For csh, add the following to the end of your ~/.cshrc:
      set path=( /cad2/ece344s/cs161/bin $path)
  2. For bash, add the following to the end of your ~/.bashrc:
      export PATH=/cad2/ece344s/cs161/bin:$PATH

Then log out and log back in. Run echo $PATH and you should see the new path.

At this point, you should follow the instruction under Remote Repository. Later in lab 3 and 4, when you work with another group member, the other group member should follow the instruction under Cloning Repository after the remote repository has been created.

Setting up Remote Repository

  1. Make a directory in which you will do all your work. For the purposes of the remainder of this assignment, we'll assume that it will be called ~/ece344.
      % mkdir ~/ece344
      % cd ~/ece344
      % mkdir os161
      % cd os161
      % git init
    

    The last command should show the following:

    Initialized empty Git repository in ~/ece344/os161/.git/
    
  2. At this point, we (i.e., the instructor and the TAs) cannot access this repository because it is local within your home directory (that should not be accessible to others). Instead, you will submit your assignment code by creating a clone (copy) of your repository to a remote repository location that is accessible to you and to us.

    You need to run these instructions once to setup your remote repository. After that, you can update the remote repository by following the instructions in the next step.

    Each of you has been assigned a group number for this course. For lab 1 and 2, you work in a group with only yourself, while in lab 3 and 4 there are two members in each group. Below, we use XXX to denote your 3-digit group number. The remote repository will be located on the ug250.eecg.utoronto.ca machine. Log in this machine.

    ssh ug250.eecg.utoronto.ca
    
  3. Now create your repository as follows:

    cd /srv/ece344s/os-XXX
    mkdir ece344
    chmod 770 ece344
    cd ece344
    git init --bare --shared=group
    

    Make sure to replace XXX with your 3-digit (not 2-digit) group number. The chmod command above ensures that the repository will be accessible to the instructor and the TAs but not to other students. The git init command creates an empty Git repository:

    Initialized empty shared Git repository in /srv/ece344s/os-XXX/ece344/
    

    If this command returns an error, either you did not specify your group number correctly, or your remote repository exists already (e.g., your partner has already created it). If you really need to remove your repository (generally, a really bad idea), you will need to log into the remote repository machine and remove the repository directory.

  4. Now, you are done with this machine, so exit out of it. Back on your local machine, you need to tell the local repository about the location of the remote repository.

    cd ~/ece344/os161/
    git remote add origin ssh://ug250.eecg.toronto.edu/srv/ece344s/os-XXX/ece344
    

    The command shown above is run in your local repository. It associates the name origin with the remote repository. You can see that by using the git remote command.

    git remote -v
    

    It will show the following:

        origin  ssh://ug250.eecg.toronto.edu/srv/ece344s/os-XXX/ece344 (fetch)
        origin  ssh://ug250.eecg.toronto.edu/srv/ece344s/os-XXX/ece344 (push)
    
  5. You are now ready to make the first commit to your remote repository. Unpack the OS161 distribution (available in the course directory) by typing
      
    % cd ~/ece344/os161
    % tar xzf /cad2/ece344s/tester/src/os161-1.11a.tar.gz
    
  6. Your next step is to stage the starter code for commit, which means to add the files into a list of files to be committed.

    git add .
    

    Now, type git status to see what happens:

    On branch master
    
    Initial commit
    
    Changes to be committed:
      (use "git rm --cached <file>..." to unstage)
    
    	new file:   .gitignore
    	new file:   CHANGES
    	new file:   COPYING
        ...
    
  7. The starter code is now ready to be committed, which means the repository will track the files and possibly keep multiple versions of them for your debugging or auditing purposes. To commit the all staged files, type the following:

    git commit -m "added starter code"
    

    The "-m" option allows you to associate a message about what changed to the commit. You should see a large message that says 546 files changed, 43001 insertions, followed by a list of the files created.

  8. Now we want to push the changes that we made to the remote repository, thus making it available to your partner and us. You can do so with the following command:

    git push origin master
    

    You should see something like this:

    Counting objects: 640, done.
    Delta compression using up to 4 threads.
    Compressing objects: 100% (638/638), done.
    Writing objects: 100% (640/640), 384.18 KiB | 0 bytes/s, done.
    Total 640 (delta 51), reused 0 (delta 0)
    remote: Resolving deltas: 100% (51/51), done.
    To ssh://ug250.eecg.toronto.edu/srv/ece344s/os-XXX/ece344
     * [new branch]      master -> master
    

    Note that from now on, you do not need to specify "origin master" to push to remote repository anymore. Simply git push is sufficient.

  9. To start the assignment, use the git tag command to tag the current version of the repository so that you can later reference this initial version of the code base by name for commands such as git diff or git checkout. Read the Git-tag Manual Page for more information about git tags.
    % cd ~/ece344/os161
    % git tag asst1-start
    % git push --tags
    
    You will later use git tags to submit your assignment, so become familiar with this command.

Note that in addition to OS161, you can also download the distributions for System/161, the machine simulator, and the OS161 toolchain. If you are developing on the lab machines, you do not need these additional files, as they are already installed. If you wish to develop on your home machine at home, you will need to download, build, and install this package as well. Note that we do not provide support for installing this package. Also, you must ensure that your assignment works on the lab machines.

You're done! It is now time for your partner to get a copy of the repository.

Checkout Code by Cloning Repository

After the remote repository setup is complete, the second partner can clone a copy of the OS161 code in their own directory as follows:

  1. You will first need to create the ~/ece344 directory, if it does not exist already:
    % cd ~
    % mkdir ece344
    
  2. Now, clone the remote repository into the os161 folder (replace XXX with your group number):
    % cd ~/ece344
    % git clone ssh://ug250.eecg.toronto.edu/srv/ece344s/os-XXX/ece344 os161
    

    This will create a local repository in ~/ece344/os161. Now both you and your partner have a copy of the repository and are connected to the same remote repository.

  3. When any one of you pushes new commits to the remote repository, the other partner will need to update his/her local repository with your changes. This can be done as such:
    % cd ~/ece344/os161
    % git pull
    
    Note that it is possible that your partner's commits will conflict with yours. In this case, you will need to merge your changes. Merging requires that you carefully consider your changes and decide which one should be used. Git will alter all files in conflict such that each file shows what you need to manually handle. For more information, please read the Git-merge Manual Page.

Code reading

One of the challenges of OS161 is that you are going to be working with a large body of code that was written by someone else. When doing so, it is important that you grasp the overall organization of the entire code base, understand where different pieces of functionality are implemented, and learn how to augment it in a natural and correct fashion. As you and your partner develop code, although you needn't understand every detail of your partner's implementation, you still need to understand its overall structure, how it fits into the greater whole, and how it works.

In order to become familiar with a code base, there is no substitute for actually sitting down and reading the code. Admittedly, most code makes poor bedtime reading (except perhaps as a soporific), but it is essential that you read the code. It is all right if you don't understand most of the assembly code in the codebase; it is not important for this class that you know assembly.

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, we strongly recommend that you look over at least every file in the kernel.

The key part of this exercise is understanding the base system. Your goal is to understand how it all fits together so that you can make intelligent design decisions when you approach future assignments. This may seem tedious, but if you understand how the system fits together now, you will have much less difficulty completing future assignments. Also, it may not be apparent yet, but you have much more time to do so now than you will at any other point in the term.

The file system, I/O, and network sections may seem confusing since we have not discussed how these components work. However, it is still useful to review the code now and get a high-level idea of what is happening in each subsystem. If you do not understand the low-level details now, that is OK.

These questions are not meant to be tricky -- most of the answers can be found in comments in the OS161 source, though you may have to look elsewhere (such as Tanenbaum) for some background information. Make sure that you can answer these questions, which may be asked during the midterm or the final exams.

Top level directory

In the top-level os161 directory (created by the checkout above), you will find the following files:

configure: top-level configuration script; configures the OS161 distribution, including all the provided utilities, but does not configure the operating system kernel.

Makefile: top-level makefile; builds the OS161 distribution, including all the provided utilities, but does not build the operating system kernel.

You will also find the following directories:

kern: the kernel source code.

lib: user-level library code lives here. We have only two libraries: libc, the C standard library, and hostcompat, which is for recompiling OS161 programs for the host UNIX system. There is also a crt0 directory, which contains the startup code for user programs.

include: these are the include files that you would typically find in /usr/include (in our case, a subset of them). These are user level include files; not kernel include files.

testbin: these are pieces of test code.

bin: all the utilities that are typically found in /bin, e.g., cat, cp, ls, etc. The things in bin are considered "fundamental" utilities that the system needs to run.

sbin: this is the source code for the utilities typically found in /sbin on a typical UNIX installation. In our case, there are some utilities that let you halt the machine, power it off and reboot it, among other things.

man: the OS161 manual ("man pages") appear here. The man pages document (or specify) every program, every function in the C library, and every system call. You will use the system call man pages for reference in the course of assignment 2. The man pages are HTML and can be read with any browser.

mk: fragments of makefiles used to build the system.

You needn't understand all the files in bin, sbin, and testbin now, but you certainly will later on. In fact, you will be adding a directory to testbin in this assignment. Eventually, you will want to modify other files in these directories and/or write your own utilities and these are good models. Similarly, you need not read and understand everything in lib and include, but you should know enough about what's there to be able to get around the source tree easily. The rest of this code walk-through is going to concern itself with the kern subtree.

The kern subdirectory

Once again, there is a Makefile. This Makefile installs header files but does not build anything.

In addition, we have more subdirectories for each component of the kernel as well as some utility directories. kern/arch: This is where architecture-specific code goes. By architecture-specific, we mean the code that differs depending on the hardware platform on which you're running. There is one directory here: mips which contains code specific to the MIPS processor.

kern/arch/mips/conf/conf.arch: This tells the kernel config script where to find the machine-specific, low-level functions it needs (throughout kern/arch/mips/*).

Question 0. What is the default compile option that we use for OS161's virtual memory system?

kern/arch/mips/include: This folder and its subdirectories include files for the machine-specific constants and functions.

Question 1. In what file would you look to figure out how the various machine registers are labeled in OS161?

Question 2. 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?

kern/arch/mips/*: The other directories contain source files for the machine-dependent code that the kernel needs to run. Most of this code is quite low-level.

Question 3. What will happen if you try to run on a machine with more than 512 MB of memory?

Question 4. What bus/busses does OS161 support?

kern/compile: This is where you build kernels. In the compile directory, you will eventually find one subdirectory for each kernel you want to build. In a real installation, these will often correspond to things like a debug build, a profiling build, etc. In our world, each build directory will correspond to a programming assignment, e.g., ASST1, ASST2, etc. These directories are created when you configure a kernel (described in the next section). This directory and build organization is typical of UNIX installations and is not universal across all operating systems. kern/conf: config is the script that takes a config file, like ASST1, and creates the corresponding build directory (shown later).

kern/include: 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 5. What would splx(splhigh()) do?

Question 6. Why do you think types.h defines explicitly-sized types such as int32_t instead of using the shorter int?

Question 7. What about type names such as __time_t? What other purpose might these type definitions serve?

Question 8. What is the interface to a device driver (i.e., what functions must you implement to add a new device)?

Question 9. What is the easiest way to add debug messages to your operating system?

Question 10. What synchronization primitives are defined for OS161?

Question 11. What is the difference between a thread_yield and a thread_sleep?

Question 12. What version of OS161 are you running? Why might this be important to know?

kern/lib: These are library routines used throughout the kernel, e.g., arrays, kernel printf, etc.

kern/main: This is where the kernel is initialized and where the kernel main function is implemented.

kern/thread: Threads are the fundamental abstraction on which the kernel is built (do not forget to look back at header files!)

Question 13. What data structure do we use to keep track of the runnable threads in the system?

Question 14. Which synchronization primitives are completely provided for you? (Guess when the others will exist.)

Question 15. What is a zombie?

kern/asst1: This is the directory that contains the framework code that you will need to complete assignment 1. You can safely ignore it for now.

kern/userprog: This is where you will add code to create and manage user level processes. As it stands now, OS161 runs only kernel threads; there is no support for user level code. In Assignment 2, you'll implement this support.

kern/vm: This directory is also fairly vacant. In Assignment 3, you'll implement virtual memory and most of your code will go in here.

Question 16. What is the purpose of functions like copyin and copyout in copyinout.c? What do they protect against? Where might you want to use these functions?

kern/dev: This is where all the low level device management code is stored.

Question 17. Look at how getch is implemented. It is the function for reading a character from the terminal. Which function in the kernel will the hardware call when a character is received from the terminal?

kern/fs: The file system implementation has two directories. We'll talk about each in turn. kern/fs/vfs is the file-system independent layer (vfs stands for "Virtual File System"). It establishes a framework into which you can add new file systems easily. You will want to go look at vfs.h and vnode.h before looking at this directory.

Question 18. What happens when you do a read on /dev/null?

Question 19. What lock protects the current working directory?

kern/fs: This is where the actual file systems go. The subdirectory sfs contains a simple default file system. You will augment this file system as part of Assignment 4, so we'll ask you more questions about it then.

Question 20. The vnode layer is file system independent; why is there a file sfs_vnode.c in the sfs directory? What is the purpose of the routines in that file?

Building a kernel

Now it is time to build a kernel. You will need to configure a kernel and then build it.

  1. Configure your tree for the machine on which you are working.
      % cd ~/ece344/os161
      % ./configure --werror --ostree=$HOME/ece344/build
    
    Note the use of $HOME instead of ~. The --ostree option specifies the root of the OS tree, and the --werror option will turn compiler warnings into errors. All programs will be installed in the build folder after the next step. Within the simulator, the root directory within the build folder will be accessible as the / directory. For example, programs in the bin directory will be installed in ~/ece344/build/root/bin and accessible as /bin within the simulator. ./configure --help explains other configure options.
  2. Now let's build and install the user level utilities. If you have any compilation issues, make sure that your $PATH variable is set correctly as described above.
      % make
    
  3. Now for the kernel. Configure a kernel named ASST1.
      % cd ~/ece344/os161
      % cd kern/conf
      % ./config ASST1
    
    This will create the ASST1 build directory in kern/compile. The next step will actually build a kernel in this directory. Note that you should specify the complete pathname ./config when you configure OS161. If you omit the ./, you may end up running the configuration command for the system on which you are building OS161, and that is almost guaranteed to produce rather strange results!
  4. Build and install the ASST1 kernel.
      % cd ~/ece344/os161/kern
      % cd compile/ASST1
      % make depend
      % make
      % make install
    

Running your kernel

  1. Change to your build directory. Copy the assignment 1 simulator configuration file into the build directory.
      % cd ~/ece344/build
      % cp /cad2/ece344s/tester/sysconfig/sys161-asst1.conf sys161.conf
    
    You should take a look at this file, as it describes how to configure the simulator you will be running your code in.
  2. Run the machine simulator on your operating system.
      % sys161 kernel
    
  3. At the OS161 command prompt, run the poweroff command that tells the system to shut down as follows.
      OS/161 kernel [? for menu]: p /sbin/poweroff
    
    Note that the p command in OS161 is used to run a program.

Deliverable 1: Practice modifying your kernel

Locate the code that prints the name of your group and change it to print anything else other than the current message. For example:

sys161: System/161 release 1.14, compiled Jan 13 2015 13:55:47

OS/161 base system version 1.11a
Copyright (c) 2000, 2001, 2002, 2003
   President and Fellows of Harvard College.  All rights reserved.

My-Little-Pony's system version 0 (ASST1 #1)

Cpu is MIPS r2000/r3000
348k physical memory available
Device probe...
lamebus0 (system main bus)
...

Note that the name of the fictitious group is My-Little-Pony, in case you are not sure what got changed.

Using GDB

  1. For using gdb, you will need two windows (terminals). If you are logged in remotely, now is a good time to learn about the screen command, which will make it easier for you to work with multiple windows. Run the kernel in gdb by first running the kernel in the run window, and then attach gdb to the kernel from the debug window.
      (In the run window:)
      % cd ~/ece344/build
      % sys161 -w kernel
    
      (In the debug window:)
      % cd ~/ece344/build
      % cs161-gdb kernel
      (gdb) target remote unix:.sockets/gdb
      (gdb) break menu
      (gdb) c
         [gdb will stop at menu() ...]
      (gdb) where
         [displays a nice back trace...]
      (gdb) detach
      (gdb) quit
    

Practice with Git

In order to build your kernel above, you already checked out a source tree. Now we'll demonstrate some of the most common features of Git.

  1. First, make sure that you have completed updating your working copy to include the necessary file(s) from deliverable 1 above. git status is your friend here. Run this command in ~/ece344/os161. You should only see files that you have modified or added to your working copy.
  2. From within your ~/ece344/os161/kern directory, execute:
      % git status
    
    Note that the files you changed shows status 'modified'.
  3. Execute
      % cd ~/ece344/os161/
      % git diff
    
    to display the differences between your changes versus the previously committed version.
  4. Now stage your changes with the add command:
    git add :/
    :/ means everything in the repository that has been created, modified, or deleted. Alternatively, you can specify the -u option to only stage only existing files (modified or deleted). You can also specify a subdirectory or specific file with git add, e.g.:
    git add path/to/file
    
    You may wish to consult the git-add Manual Page for more information.
  5. Now commit your changes using git commit. This command will commit your changes. The command invokes an editor so that you can add a log message. For short commit messages, you can use the -m option of commit.
  6. Push your changes to the remote repository so that your partner will have access to it.
    % git push
    
  7. As an exercise, try removing the first 100 lines of kern/main/main.c.
  8. Try to build your kernel (this ought to fail).
  9. Realize the error of your ways and get back a good copy of the file.
    % cd ~/ece344/os161/kern
    % git checkout main/main.c
    
  10. Try to build your tree again.
  11. Now, examine the DEBUG macro in lib.h. Based on your earlier reading of the operating system, try adding ten useful debugging messages to your operating system.
  12. Now, see where you inserted these DEBUG statements by doing a diff.
      % cd ~/ece344/os161/kern
      % git diff
    

Deliverable 2: Changing the OS menu

When you run your kernel under the simulator, typing ? shows a kernel menu. This menu allows running various commands. For example, ?t shows various tests that you can run.

In this part of the lab, you will add some new options to the OS menu.

The DEBUG macro uses the dbflags variable in the kernel. Depending on the value of this variable, different types of debugging messages are printed. For example, if its value is 0x012, then DB_SYSCALL and DB_THREADS messages are printed. Why?

The problem is that if you want to see different types of messages, the dbflags variable has to be changed and the kernel has to be recompiled. The reason you may want to see different types of messages is that printing all types of messages may make it harder to debug a specific problem.

Your task is to allow changing the value of the dbflags variable from the OS menu.

First, find out the initial value of this variable. Then change the OS menu code (where is it located?) so that the menu output is as follows:

OS/161 kernel [? for menu]: ?o

OS/161 operations menu
    [s]       Shell                     [pf]      Print a file
    [p]       Other program             [cd]      Change directory
    [dbflags] Debug flags               [pwd]     Print current directory
    [mount]   Mount a filesystem        [sync]    Sync filesystems
    [unmount] Unmount a filesystem      [panic]   Intentional panic
    [bootfs]  Set "boot" filesystem     [q]       Quit and shut down

Operation took 0.058339600 seconds
OS/161 kernel [? for menu]: dbflags

OS/161 Debug flags
    [df 1 on/off]        DB_LOCORE      [df 7 on/off]        DB_EXEC
    [df 2 on/off]        DB_SYSCALL     [df 8 on/off]        DB_VFS
    [df 3 on/off]        DB_INTERRUPT   [df 9 on/off]        DB_SFS
    [df 4 on/off]        DB_DEVICE      [df 10 on/off]       DB_NET
    [df 5 on/off]        DB_THREADS     [df 11 on/off]       DB_NETFS
    [df 6 on/off]        DB_VM          [df 12 on/off]       DB_KMALLOC

Current value of dbflags is 0x0
Operation took 0.058040000 seconds

Note that ?o produces a new option [dbflags]. Typing dbflags produces the new menu. With this menu, typing df 5 on will turn on DB_THREADS messages, and typing df 5 off will turn off these messages.

OS/161 kernel [? for menu]: df 5 on
Operation took 0.000024960 seconds
OS/161 kernel [? for menu]: dbflags

OS/161 Debug flags
    [df 1 on/off]     DB_LOCORE         [df 7 on/off]     DB_EXEC
    [df 2 on/off]     DB_SYSCALL        [df 8 on/off]     DB_VFS
    [df 3 on/off]     DB_INTERRUPT      [df 9 on/off]     DB_SFS
    [df 4 on/off]     DB_DEVICE         [df 10 on/off]    DB_NET
    [df 5 on/off]     DB_THREADS        [df 11 on/off]    DB_NETFS
    [df 6 on/off]     DB_VM             [df 12 on/off]    DB_KMALLOC

Current value of dbflags is 0x10
Operation took 0.062876880 seconds

If you type df 3 on after the code shown above, the dbflags value should then be 0x14. This part of the lab will help you exercise bit manipulation skills, which will be useful in later labs.

Your code should also ensure that the arguments to df are passed correctly, or else your code should print the following in a separate line:

Usage: df nr on/off

Deliverable 3: System Calls (OPTIONAL)

Please note that deliverable 3 is optional. It is not for marks and is intended for you to get ahead in the later assignments if you so choose. These system calls, however, are required for assignment 3 and 4, so finishing them now can save you some time down the road. The os161-tester does provide test cases for them, but they do not affect your marks for this assignment. It is important to note that you must finish deliverable 1 and 2 to get full marks for this assignment.

In a real operating system, the main purpose of a kernel is to provide support for user-level programs. Most such support is accessed via system calls. We have provided you with the reboot system call. In this assignment, you will implement some simple system calls.

The full range of system calls that we think you might want over the course of the semester is listed in kern/include/kern/callno.h. For this assignment, you need to design and implement the following system calls:

  • _exit

    Right now, when you run any program (not a command), you will see the message: Unknown syscall 0. You can try this by running a dummy program with the command: p /bin/true. This occurs because the program calls the _exit() system call when it is finished, but you have not implemented the system call yet. You will revisit this system call in assignment 3. For now, write some placeholder code for this system call to suppress the warning.

  • write

    When you run some of the test programs, you will notice that printf() and other console-printing libc calls do not work. You will need to implement a system call to make printf() and other console-printing calls work. You should first figure out which system call is eventually used to perform the printing, and implement this system call. (Hint: how does the kernel itself print messages?) The implementation of this system call should be straightforward. However, its correctness is critical: if it does not work properly, many tests will fail in very confusing ways. Therefore we strongly suggest that you test it in isolation and only move on after you're confident about its correctness.

  • read

    To get some of the testing programs to work, you will also need to implement a read system call that reads one character at a time. The implementation of this system call should be straightforward. However, similar to the printing system call, its correctness is critical. Therefore we strongly suggest that you test it in isolation and only move on after you're confident about its correctness.

    You are only required to support reading in one character at a time. In other words, if the length of the buffer is not 1, then you may return the error code EUNIMP, which means "not implemented".

  • sleep

    This system call allows a thread go to sleep (i.e., not be scheduled).

  • __time

    This system call returns the current time down to the nanoseconds. It is later used by the tester to measure performance of your implementation.

To successfully implement system calls, you will need to modify machine-specific code which handles switching from userspace to the kernel. You should study the function mips_syscall under os161/kern/arch/mips/mips. As a starter, look at how it implements the reboot system call and first try to implement the _exit system call. The function prototype of each system call is explained in their respective manual page under os161/man/syscall. For most of these system calls, you should be able to find very similar functions that have already been implemented in the kernel (e.g. check out os161/kern/include/lib.h). All you need to do is to call these respective functions to implement the system calls.

For system calls that have pointer arguments (e.g., write, read, __time), you need to be very careful with handling them. A userspace program must not be allowed to crash the kernel by passing invalid pointer arguments. Study the use of copyin and copyout in the uiomove function under os161/kern/userprog. Also read the comments at the top of os161/kern/lib/copyinout.c.

Assignment Submission

Finally, you need to submit your code for this assignment.

  1. Once you are confident that your assignment is done, run make clean from the os161 directory. This will clean all generated files. Then use git status in the os161 directory to find out the status of all files. Make sure to commit and push all files before continuing with the submission process. Also make sure that your partner's changes are also committed.
  2. To submit your assignment, tag your repository for the end of assignment 1 and push the tags to the remote repository:
    % git tag asst1-end
    % git push --tags
    
  3. If you need to re-submit (before the deadline), delete the existing tag and push a new version of the tag after you committed your new changes:
    % git push  # push your new changes to remote repository first
    
    % git tag -d asst1-end      # delete old tag
    % git tag asst1-end         # create new tag
    % git push --tags --force   # force remote repository to update to new tag
    

Testing your assignment with the autotester

Please read the instructions for testing your code. We will run identical tests to mark your assignment, so make sure to use the tester to check your progress. After submission, you are suggested to run the tester in marker mode to verify that your submission has been received correctly.

Note that because deliverable 3 is optional, you may end up with more marks than the total (e.g. 50/12). Please make sure you complete name and dbflags as they are mandatory for deliverable 1 and 2. Failure to do so will result in loss of marks, even if you completed deliverable 3.