You will need to submit your final kernel implementation by email. You will turn in an archive that contains all of your code for the kernel implementation and associated programs, as well as a short self-evaluation that you should complete with your partner. Your self evaluation will be based on the same rubric that I will use to grade your kernel project.
Your kernel implementation will be evaluated on ten components, described below in detail. For each component, you will receive a score of 3, 2, 1, or 0. The rubric below lists the expectations for each component and score. Your grade on the kernel implementation will be the sum of your scores on this rubric, divided by 30 (the maximum possible score).
| Component | 3 | 2 | 1 | 0 |
|---|---|---|---|---|
| 1. Input/Output | The kernel contains a working kprintf function with support for the %c, %s, %d, %x, and %p flags. The kgetc function correctly returns printable characters from a ring buffer populated by the keyboard interrupt handler. The kgetc function blocks until keyboard input is available. | Both kprintf and kgetc work in most cases, but small bugs lead to unexpected behavior in edge cases. | Both I/O functions are buggy enough that they do not work in the common case, but the implementation is on the right track. | At least one I/O function was not implemented, or has an overly-simplified implementation that does not attempt to handle common-case behavior. |
| 2. Exceptions | The kernel sets exception handlers for all standard exceptions in the Intel manual. Each handler prints the name of the exception when it occurs, along with an error code whenever appropriate. | The kernel sets all required exception handlers, but does not report which exception occurred in at least some cases. | The kernel does not catch exceptions in most or all cases because of a bug in the implementation. | Exception handling is incomplete or was not attempted. |
| 3. Virtual Memory | The vm_map function correctly maps pages into an address space with specified permissions. All memory accesses to physical addresses are made through the HHDM, and permissions are limited only in the last-level page table. | The vm_map function works in most cases, but sets permissions incorrectly (e.g. not in the last-level table), directly accesses physical addresses instead of using the HHDM, or has other bugs that prevent it from working. These bugs may be in vm_map itself or in a supporting function like pmem_alloc. | The vm_map function does not work, but the broken code demonstrates sufficient understanding of the address translation process that the implementation could be fixed given more time. | The vm_map function is incomplete or does not work because of a critical misunderstanding about virtual memory. |
| 4. System Calls | The kernel has working system calls to read from keyboard, write to the terminal, and map anonymous memory into an address space. Other system calls are present where appropriate, but operations that could be performed entirely in user space do no have a system call. | The three required system calls are implemented, but the kernel provides one or more system calls that could have been replaced entirely by a user space implementation (i.e. they do not rely on any privileged operations the kernel would have to perform). | At least one system call works correctly, but one or both of the others is broken or missing. | All three required system calls are missing or broken. |
| 5. Loader | After initial setup, the kernel loads and executes the init program. The loader sets up memory regions with the memory protections (i.e. read-only or non-executable) requested in the ELF file. | The kernel loads and correctly executes the init program, but is missing a small detail like setting memory protections. | The kernel is able to load and run the init program, but the program does not behave as expected (e.g. it prints garbage text instead of a string constant). | The loader is incomplete or broken in such as way that it never successfully reaches the init program's entry point, or running the init program causes a system exception. |
| 6. Standard Library | The kernel, init and any other programs link against a standard library. That standard library contains useful utility functions like printf, strlen, memcpy, etc. instead of duplicating them. Note that printf will likely duplicate much of kprintf, but these two functions cannot easily be combined. | One or two helper functions are duplicated across the project, but most have been moved to the standard library. | The standard library contains some useful functions. Several others should be moved to the standard library, or the functions that were moved are not used consistently throughout the project. | The standard library is minimal, mostly unused, or completely missing. |
| 7. User Mode | The init program is executed in user mode rather than kernel mode. This can be verified by making accesses to privileged virtual addresses (e.g. anything in the higher half), which should cause a page fault. | An attempt was made to switch to user mode, but the final jump to user mode did not work. The cause of the error was a subtle mistake in setting up the GDT/IDT, or in the loader. | An attempt was made to switch to user mode, but large required pieces of the switch are broken or missing. | Switching to user mode was not attempted. |
| 8. Processes | The kernel provides an exec system call that unmaps the lower half and loads a new executable into the address space. When that program exits (with an exit system call) the kernel re-loads the init program. | Processes are partially working. The exec function may rely on a hard-coded program name or exit might not work. The implementation is missing functionality, but doesn't crash. | Processes are not working, but the implementation shows serious effort toward completing at least the exec system call. | The exec and exit system calls were not attempted, or show minimal effort. |
| 9. Shell | The init program launches a shell that accepts user commands. Invalid commands are reported to the user, and valid commands that name an executable that can be run execute that program. | The shell accepts user commands and, in at least some cases, is able to start new programs to run them. Some valid commands do not work, or the shell may not start again when a program exits. | The implementation provides shell-like functionality, but the shell does not actually run programs when valid commands are typed. The shell may be broken, but at least shows significant effort. | A shell implementation was not attempted, or the implementation provided is non-functional and shows minimal effort. |
| 10. Code Quality | Code is well-organized, easy to read, and commented to a degree that would make its functionality clear to any other student in this class. Code is organized across files in a logical manner that keeps individual files relatively short and readable. | Code is mostly well-organized, easy to read, and commented. Some limited areas of the project are disorganized, unclear, or uncommented, but these are the exception rather than the rule. | The majority of the project's code is missing comments, unclear, or disorganized. There are several examples of extremely long files or functions, or large blocks of code that should be commented but are not. | There is no discernable organizational scheme, comments are uninformative or missing entirely, and formatting of code makes it difficult to read across the entire project. |
Before you turn in your kernel, I would like each group to complete a short self-evaluation to include with your implementation.
In a file named self-evaluation.txt, please assign yourself a score for each of the ten components in the rubric above.
Along with each assigned score, please justify your choice;
if you chose a 3, how can you demonstrate that all of the expected elements for this component are working?
If you assigned a 2 or 1, what works and what does not?
If you are assigning a score that requires you to show significant effort, what is the evidence of this effort?
What did you attempt, even if it didn’t work out?
You should expect to write at least two or three sentences for each component that you attempted.
Note that I may not agree with your self-assessment; our scores may differ so the grade I assign on your kernel implementation might be different from your self-assessment. But my hope is that we’ll agree in most cases. Your self-assessment will help me find and account for work you put into the kernel implementation, especially in cases where you attempted but didn’t quite finish all of the parts of a kernel component.
Place the self-evaluation.txt file in your kernel project directory, and then create a .zip or .tar.gz file of your entire kernel project (including the self evaluation).
Send that archive to me by email as an attachment, and CC your partner (if you have one) on the email.
If you are on a linux machine, you can create an archive with the following command:
$ tar cvzf kernel.tar.gz ~/path/to/your/project
You will need to replace ~/path/to/your/project with the path (starting from your home directory) that takes you to the top-level directory containing our kernel implementation.
Please make sure you archive the top-level directory that contains the kernel, init, and other subdirectories, not just the kernel implementation subdirectory.
Please make every effort possible to turn in a kernel implementation that builds and runs; you may have partially-implemented functionality that is turned off, but that is okay. The self-evaluation is an opportunity for you to describe that partial implementation. If your kernel boots and runs it will be much easier for me to test the features you’ve implemented successfully, and to understand where you ended up on partially-completed components.