Lab 0: Hello osv

Aaron Bauer

January 2, 2022

Lab 0: Hello osv

Important Deadlines

Introduction

All of our labs are based on osv. osv is a new experimental operating system kernel for teaching the principles and practice of operating systems. osv’s baseline code is a complete, bootable operating system. It provides some simple system calls that it is capable of running a minimal shell program. This lab is about exploring the osv codebase and generally getting oriented. Your task for futures labs will be to make osv complete and also add a few functionalities.

Getting osv

Linux environment

You will need to be on a Linux computer to work with osv. See the section on programming environments on the course web page. If you need a refresher on navigating a Linux command line, see this tutorial or the other resources on the course web page.

Instead of developing the operating system on a real, physical personal computer (PC), we will use a program that faithfully emulates a complete PC: the code you write for the emulator will boot on a real PC too. Using an emulator simplifies debugging; you can, for example, set break points inside of the emulated x86_64 architecture, which is difficult to do with the silicon version of an x86_64 architecture. In osv, we will use the qemu Emulator, a modern and fast emulator. If you are working on your local machine (mantis already has these installed) you will likely need to install qemu with

$ sudo apt install qemu qemu-system-x86

or the equivalent if you’re using a non-Debian/Ubuntu Linux.

Cloning the git repository

To acquire the osv code that will be the basis for the labs, you will use git. I have set up the repository (i.e., a collection of files managed by git) on the CS mantis server, so you will clone the repository from there to get the code. Run the command below to do so (replacing YOUR_CARLETON_USERNAME with your own username—the part of your Carleton email before the ‘@’). If prompted about authenticity of mantis, enter yes to continue. Type in your Carleton password when prompted.

$ git clone ssh://YOUR_CARLETON_USERNAME@mantis.mathcs.carleton.edu/web-pages/www.cs.carleton.edu/faculty/awb/cs332/osv-w22

This will create a osv-w22 directory wherever you ran that command.

To learn more about git, take a look at Git resources on the course web page.

Running osv

Run make in the osv-w22 directory to build the osv kernel. Now you’re ready to run qemu. You’ll need to supply the files build/fs.img and build/osv.img, created by the make process, as the contents of the emulated PC’s virtual hard disk. Those hard disk images contain our boot loader build/arch/x86_64/boot, our kernel build/kernel/kernel.elf and a list of user applications in build/user. Fortunately, make can take care of this too. Run make qemu to run qemu with the options required to set the hard disk and direct serial port output to the terminal. Some text should appear in the terminal:

E820: physical memory map [mem 0x126000-0x1FFE0000]
 [0x0 - 0x9FC00] usable
 [0x9FC00 - 0xA0000] reserved
 [0xF0000 - 0x100000] reserved
 [0x100000 - 0x1FFE0000] usable
 [0x1FFE0000 - 0x20000000] reserved
 [0xFFFC0000 - 0x100000000] reserved

cpu 0 is up and scheduling 
cpu 1 is up and scheduling 
OSV initialization...Done

$

Press Ctrl-a x to exit qemu.

If you see warnings about TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5], you can safely ignore them.

Organization of source code

osv
├── arch(x86_64)      // all architecture dependent code for different architecture
│   └── boot          // bootloader code
│   └── include(arch) // architecture dependent header files (contain architecture specific macros)
│   └── kernel        // architecture dependent kernel code
│   └── user          // architecture dependent user code (user space syscall invocation code)
│   └── Rules.mk      // architecture dependent makefile macros
├── include           // all architecture independent header files
│   └── kernel        // header files for kernel code
│   └── lib           // header files for library code, used by both kernel and user code
├── kernel            // the kernel source code
│   └── drivers       // driver code
│   └── mm            // memory management related code, both physical and virtual memory
│   └── fs            // file system code, generic filesys interface (VFS)
│       └── sfs       // a simple file system implementation implementing VFS
│   └── Rules.mk      // kernel specific makefile macros
├── user              // all the source code for user applications
│   └── lab*          // tests for lab*
│   └── Rules.mk      // user applications makefile macros
├── lib               // all the source code for library code
│   └── Rules.mk      // user applications makefile macros
├── tools             // utility program for building osv
└── Makefile          // Makefile for building osv kernel

After compilation (make), a new folder build will appear, which contains the kernel and fs image and all the intermediate binaries (.d, .o, .asm).

Part 1: Debugging osv

The purpose of the first exercise is to get you started with qemu and qemu/gdb debugging.

A Note on x86_64 Assembly

The definitive reference for x86_64 assembly language programming is Intel’s instruction set architecture reference is Intel 64 and IA-32 Architectures Software Developer’s Manuals. It covers all the features of the most recent processors that we won’t need in class but you may be interested in learning about. An equivalent (and often friendlier) set of manuals is AMD64 Architecture Programmer’s Manual. Save the Intel/AMD architecture manuals for later or use them for reference when you want to look up the definitive explanation of a particular processor feature or instruction.

You don’t have to read them now, but you may want to refer to some of this material when reading and writing x86_64 assembly.

gdb

gdb can be used as a remote debugger for osv. We have provided a gdbinit file for you to use as ~/.gdbinit. It connects to a port that qemu will connect to when running make qemu-gdb and loads in kernel symbols. You can generate your ~/.gdbinit using the following command.

$ cp arch/x86_64/gdbinit ~/.gdbinit

To attach gdb to osv, you need to open two separate terminals. Both of them should be in the osv-w22 root directory. In one terminal, type make qemu-gdb. This starts the qemu process and wait for gdb to attach. In another terminal, type gdb. Now the gdb process is attached to qemu.

In osv, when bootloader loads the kernel from disk to memory, the CPU operates in 32-bit mode. The starting point of the 32-bit kernel is in arch/x86_64/kernel/entry.S. entry.S sets up 64-bit virtual memory and enables 64-bit mode. You don’t need to understand entry.S. entry.S jumps to main function in kernel/main.c which is the starting point of 64-bit OS.

Question #1:

After attaching the qemu instance to gdb, set a breakpoint at the entrance of osv by typing in b main. You should see a breakpoint set at main in kernel/main.c. Then type c to continue execution. osv will go through booting and stop at main. Which line of code in main prints the physical memory table? (Hint: use the n comand to have gdb execute one line of C code at a time.)

Question #2:

We can examine memory using gdb’s x command. The gdb manual has full details, but for now, it is enough to know that the command x/nx ADDR prints n words of memory at ADDR. (Note that both ‘x’s in the command are lowercase, the second x tells gdb to display memory content in hex.)

To examine instructions in memory (besides the immediate next one to be executed, which gdb prints automatically), use the x/i command. This command has the syntax x/ni ADDR, where n is the number of consecutive instructions to disassemble and ADDR is the memory address at which to start disassembling.

Repeat the previous process to break at main. What’s the memory address of main (p main)? Does gdb work with real physical addresses? Explain your answer.

Question #3:

You may have noticed that when gdb hit your breakpoint, the message specified a thread:

Thread 1 hit Breakpoint 1, main () at kernel/main.c:34

osv boots up with multiple threads, which you can see by running info threads. We’ll talk a lot more about threads in the coming weeks—the basic idea is that each thread is an independent unit of execution, which enables simultaneous computation. For now the goal is to explore gdb’s capabilities when it comes to multi-threaded programs. At the start of main, the output of the info threads command indicates that one thread is halted. What function starts it running? What main function does the second thread start in? What happens if you restart and set a breakpoint for that function?

What to turn in

Submit a file lab0.txt with your answers to the questions listed above to the CS 332 Gradescope.

Grading

This lab will be graded out of 50 points, as shown in the table below. Comments explaining your approach can help earn partial credit.

Test Points
Submission 20 points
Question #1 10 points
Question #2 10 points
Question #3 10 points