About
KidneyOS is a toy OS written in Rust, designed for teaching operating systems concepts. It is loosely based on PintOS (whose name being misread "pintos", as in the bean, is the source of the name). It is still a work in progress, and is not yet ready for use. If you're interested in using KidneyOS for teaching when it's ready, you can "watch" the repository for releases on GitHub.
KidneyOS is being developed by students at the University of Toronto. The project is supervised by Professor Jack Sun.
Building
In this section, we'll walk you through building a KidneyOS ISO from source on your computer.
Creating a Build Environment
First, we need to prepare an environment with the necessary dependencies. There are multiple approaches you can take. (Your instructor may have provided guidance on which of these options to choose.) After choosing one of these methods and completing the steps, return to this page and proceed to the next section.
- You can use a Nix devshell. This is the recommended approach. This method can only be used directly on
x86_64-linux
andaarch64-linux
hosts (but you can still use it indirectly on other host systems via WSL or a virtual machine). Once you've installed the Nix package manager, we'll use it to create the build environment with a single command. Click here for instructions. - You can run the build tools inside a Docker container. This method is likely the simplest if you're familiar with Docker, but it comes with some overhead on MacOS and Windows, and can be somewhat awkward as graphical development tools can't be run inside Docker, so you'll have to install them manually on your host system. Click here for instructions.
- You can install all the dependencies manually by using your package manager and building things from source when necessary. This method is not recommended as it is the most difficult and may result in build failures if you install different versions of tools than those used by KidneyOS maintainers. Like the first option, this method can only be used directly on
x86_64-linux
andaarch64-linux
hosts. Click here for instructions.
Building KidneyOS
Once you have a build environment ready, simply run:
make
...from within your build environment, at the root of the KidneyOS repository. This will compile the various pieces of KidneyOS, and produce an ISO at build/kidneyos.iso
.
Running KidneyOS
To run the ISO you've just built, run:
make run-qemu
There are two things to note here:
- If you're building KidneyOS with Docker, this command should be run on the host system, not in the container build environment.
- If you're building KidneyOS with any of the other methods, you don't have to run
make
first after modifying the source code.make run-qemu
will also automatically rebuild the ISO if any of the KidneyOS source files have changed.
Nix
This section describes how to build KidneyOS using the Nix package manager.
❗ This method is only supported on
x86_64-linux
andaarch64-linux
hosts. You can still use it indirectly on other host systems via WSL or a virtual machine, but installing WSL or seting up a virtual machine will be up to you. If you chose to do this, all of the following commands should be run in the WSL or virtual machine environment.
Installing Nix
If you already have Nix installed, you can skip this step. Note that the Nix package manager can be installed alongside your system's existing package manager. You don't have to uninstall your existing package manager. Instructions for installing Nix can be found here. Both a multi-user installation or a single-user installation will work.
Clone Repository
Clone the repository and cd
into the resulting directory. (Depending on how your instructor wants you to submit your work, they may have given you an alternate repository URL. If so, use that URL instead of the one below.)
git clone https://github.com/KidneyOS/KidneyOS
cd KidneyOS
Starting the "devshell"
Now that we have Nix installed and the repository cloned, we can build and run a shell containing all the dependencies (known as a "devshell" in Nix terminology). Run the following command:
nix --extra-experimental-features flakes \
--extra-experimental-features nix-command \
develop ./nix#
(The --extra-experimental-features
flags may be unnecessary depending on your Nix version and configuration.) Note that it may take a while for everything to be downloaded and built. Once it finishes, you should be in a shell containing everything needed to build and run KidneyOS. You will need to run the command each time you want to enter a shell in which you can build KidneyOS.
Direnv
This section is optional. If you'd like to avoid having to run the command in the prior section manually each time, you can accomplish this by using direnv with nix-direnv. After following the installation instructions for both projects, run:
direnv allow
...from the root of the KidneyOS repository. Now, every time you cd
into the KidneyOS repository, the "devshell" environment will automatically be made available, without you having to run any commands.
If you use an IDE, installing its direnv extension/plugin (if one is available) and setting this up to use the environment defined in the KidneyOS repository may result in better error messages and autocompletion. The VSCode extension is available here.
Docker
This section describes how to build KidneyOS using Docker.
Clone Repository
Clone the repository and cd
into the resulting directory. (Depending on how your instructor wants you to submit your work, they may have given you an alternate repository URL. If so, use that URL instead of the one below.)
git clone https://github.com/KidneyOS/KidneyOS
cd KidneyOS
Install Docker
If you already have Docker installed, you can skip this step. If you're on Linux, install the docker
package using your package manager. Otherwise, install Docker Desktop, which can be downloaded for MacOS from here or for Windows from here.
Run Container
Then execute the appropriate script. For Linux or MacOS, use the following:
scripts/run-container.bash
...or for Windows:
scripts/run-container.ps1
This will pull the container from the GitHub container registry and start it. You will need to run the container with the script above each time you want to build KidneyOS.
Install Host Tools
Since we can't run graphical tools inside the Docker container, we'll have to install the following two packages on the host:
qemu
bochs
(Optional, but recommended as it is useful for debugging. See the corresponding "Useful Tools" section for more information.)
Package Manager/From Source
This section describes how to build KidneyOS by installing dependencies using a package manager or building them from source.
❗ This method is only supported on
x86_64-linux
andaarch64-linux
hosts, or in WSL.
⚠️ The instructions that follow are designed to be as platform-agnostic as possible. However, the names of packages are not necessarily the same across different package managers. When mentioning package names, we will provide links to Repology, which you should consult to determine the specific name used by your package manager.
Clone Repository
Clone the repository and cd
into the resulting directory. (Depending on how your instructor wants you to submit your work, they may have given you an alternate repository URL. If so, use that URL instead of the one below.)
git clone https://github.com/KidneyOS/KidneyOS
cd KidneyOS
Install Rust Toolchain
Next, we will install the specific version of the Rust toolchain used by KidneyOS. We will do this using rustup. You can install it by running the command on the rustup website, or by installing the rustup
package with your package manager. Once you've done so, run the following from the root of the KidneyOS repository:
rustup override set nightly-2024-01-04-i686-unknown-linux-gnu
Running cargo --version
should now print:
cargo 1.77.0-nightly (add15366e 2024-01-02)
Install i686-unknown-linux
Build Tools
A non-exhaustive list of the binaries you'll need to install includes i686-unknown-linux-gnu-ld
and i686-unknown-linux-gnu-objcopy
. Some package managers may have a package for these. If yours doesn't, you may have to build them from source. Admittedly, this section is quite light on details, since the process is highly platform-specific. This may be updated with more details in the future.
Install Other Tools
We'll also need to install the following packages:
grub
- Make sure it's Grub version 2.
- If you're on
aarch64-linux
, you'll need to make sure you have the right libraries to buildi386-pc
ISOs. This will be highly distro-specific specific. You may have to build Grub from source.
qemu
xorriso
bochs
- Optional, but recommended as it is useful for debugging. See the corresponding "Useful Tools" section for more information.
Useful Tools
This section contains info about a few tools that might be helpful as you work on KidneyOS.
GDB
GDB is a debugger which supports a variety of programming languages. You can debug an instance of KidneyOS running inside QEMU by first starting the virtual machine with:
make run-qemu-gdb
The QEMU process will wait for GDB to attach before starting the OS.
The root of the KidneyOS repository contains a .gdbinit
file, which makes a variety of useful configuration tweaks. Before GDB will use this file though, you'll likely need to add the following to your user-level gdbinit file (which is located at ~/.config/gdb/gdbinit
on Linux and MacOS):
set auto-load safe-path KidneyOS # Replace "KidneyOS" with the path to your repository.
Then, run the following from the root of the KidneyOS repository. (If you built KidneyOS with Docker and are running QEMU on your host, and rust-gdb
is not installed on your host system, you can also use plain gdb
, though it won't have the Rust-specific pretty printers added by rust-gdb
, which is a wrapper script for gdb
.)
rust-gdb
This will start GDB. After you hit the enter key once, GDB should attach to QEMU and resume execution of the OS until it reaches the _start
function defined in trampoline/src/lib.rs
. From this point onwards, you should be able to use GDB as usual.
Bochs
Bochs is a x86 emulator. (Note that it is a GUI program, meaning if you are building KidneyOS using Docker, you'll have to install it separately on your host system.) It has some, though not all, of the debugging features that GDB does, but it also has additional features which are more specific to OS development that GDB doesn't have. Some examples include:
- It has an indicator which displays the current CPU mode.
- You can dump sections of memory based on either physical or linear addresses.
- It has windows for viewing the contents of the global and interrupt descriptor tables, and the memory page tables.
You can run KidneyOS with Bochs by running:
make run-bochs
The bochs_break!
macro (which is defined in shared/src/macros.rs
) may be of use when running KidneyOS with Bochs.
Code Coverage
If you're writing tests and want to see which lines are being executed by the tests, the Makefile provides targets that help with this. To produce an HTML coverage report, run the following:
make test report-coverage
This will run the tests and produce a report at build/coverage/index.html
.
From Bootloader to main
It's not immediately obvious when looking at the repository where the execution of code "starts". This section provides an overview of how KidneyOS gets from the bootloader (the first code the hardware runs), to its main
function.
KidneyOS currently uses Grub 2 as its bootloader. Grub is resposible for enabling various CPU features, locating the KidneyOS kernel within the ISO, loading it into memory, and jumping to its entrypoint. The machine state section of the Multiboot2 specification provides a detailed description of the state the machine is left in at this point. (Multiboot2 is the standard to which Grub 2 adheres. There are other Multiboot2-compliant bootloaders; Grub 2 is the reference implementation.)
The initial entrypoint of KidneyOS (as specified by the ENTRY
declaration in the build-support/i686.ld
linker script) is the _start
function. This function is defined in the source code in trampoline/src/lib.rs
. The trampoline is an intermediate step between the bootloader and the kernel's main
function which is responsible for setting up the address space such that all kernel virtual address can be above the 0xC0000000
offset, making it easy to distinguish kernel from user virtual addresses.
One might reasonably wonder why the trampoline is in a separate crate. Grub places the kernel in physical memory at
0x100000
. When it transfers control to the_start
function, the instruction pointer will be close to that address. We need to know at compile time what the instruction pointer will be so that we'll end up in the right place when performing absolute jumps. Thus, code within the trampoline assumes a near-0x100000
instruction pointer. But when we make it into main, we want our instruction pointer to be a near-0xC0000000
virtual address, meaning the code from main should operate based on that assumption. To produce an ELF file in which these two pieces of code respect the two different assumptions, we need to use linker scripts to place them in separate sections. While you can specify the link section of a function inline in Rust with the#[link_section = "..."]
attribute, we also need this property to apply to functions from thecore
crate that are used by the trampoline. The easiest way we could find to accomplish this was to first build the trampoline into a static library, then link it into the final binary using thekernel/src/build.rs
build script.
Once the trampoline has finished setting up the address space, it can finally jump to the kernel's main
function, passing information about the size of upper memory (which was obtained from a data structure provided by Grub), as well as the number of lines of video memory that have been printed to so far (so prior logs are not overwritten).
Frame Allocator
This assignment is still a work in progress. This page will be updated as it is worked on.
Scheduler
This assignment is still a work in progress. This page will be updated as it is worked on.
File System
This assignment is still a work in progress. This page will be updated as it is worked on.