To run the kernel, you need to have the following dependencies installed:
qemuxorrisorustcandcargo- You need to have the
x86_64-unknown-nonetarget installed, and nightly rust.rustup override set nightly rustup target add x86_64-unknown-none # This isn't strictly necessary, but it's a good idea to update installed toolchains rustup update
- You need to have the
Then, you can run the kernel with the following command from the root of the repository:
cargo runThis will build the kernel, generate an image, and run it in QEMU.
The project is split into several crates:
kbuild: A library for building the kernel. Used in build.rs and the test runner.kernel: The main kernel crate.kproc: Various procedural macros used in the kernel.kerial: A qemu serial driver for the kernel. Used for debugging.krun: A test runner and QEMU launcher for the kernel.kalloc: A memory allocation library for the kernel.cake: A swiss army knife for the kernel. Contains various utilities and helpers.kelp: A library for parsing ELF files. Used in the kernel.
Some other folders that appear in the root of the repository:
boot_cfg: Configuration files for limine.boot_images: ISO images generated by the build process.output: Files generated bykserialcommands.tools: Various python scripts used for debugging. Both scripts are currently old and unused.
SmartCheck is a cargo check wrapper to force specific targets for individual crates in a workspace.
This is useful for cross-compilation scenarios where different crates may need to be checked
with different target architectures or configurations.
SmartCheck is set as the workspace's default rust-analyzer check command and will be used by default in Visual Studio Code.
Almost all debugging is done through setting various environment variables that control krun and QEMU behavior.
GDB can be automatically launched when starting the kernel in QEMU by setting the DEBUG environment variable. Depending on your setup, GDB settings may have to be customized to work. You can do this by modifying the gdb.toml file in the root of the repository (created by running with DEBUG).
# Default GDB configuration
[connection]
host = "localhost"
port = 1234
invocation = "gdb"
UEFI- If set to any value, QEMU will boot in UEFI mode using OVMF firmware.
NO_DISPLAY- If set to any value, QEMU will run without a graphical display.
KERNEL_IMAGE_PATH- Specifies the path to the kernel image to boot. If not set, the default image generated by the build process will be used.
QEMU_MEM- Specifies the amount of memory to allocate to QEMU. This can be any format that QEMU supports, such as "512M", "2G", etc.
- This can also include hotpluggable memory settings, using the same syntax as QEMU.
DEV_EXIT- If set to any value, the QEMU dev exit device will be enabled. This allows the kernel to exit QEMU by writing to a specific I/O port.
QEMU_EXTRA_ARGS- Specifies extra arguments to pass to QEMU. Arguments should be space-separated.
VERBOSE- If set to any value, krun will print out the full QEMU command line before executing it.
NO_SPAWN_GDB- If set to any value, krun will not automatically spawn GDB when started with debugging enabled.
QEMU_PATH- Specifies the path to the QEMU binary to use. If not set, the default
qemu-system-x86_64will be used.
- Specifies the path to the QEMU binary to use. If not set, the default
SMP_CORES- Controls the number of CPU cores to allocate to QEMU.
Control debugger behavior.
If set to "1", "true", or "yes", QEMU will wait for GDB to attach.
If set to "nowait", "no-wait", or "no_wait", QEMU will start a GDB server but not wait for a debugger to attach.
Specifies QEMU debug options. Flags should be comma-separated.
You can list valid flags by running qemu-system-x86_64 -d help.
You can list trace flags by runningqemu-system-x86_64 -d trace:help. (Warning: there are almost 5000 trace flags.)
Trace flags must be prefixed with trace: but the following shorthands are supported:
t/->trace:trace/->trace:t:->trace:
ALLOC_DEBUG: If set, will print out information about memory allocations and deallocations.REINSTALL_LIMINE: If set, limine will be re-downloaded from it's git repository and reinstalled.ARTIFACT_DIR: The directory to store build artifacts in.LIMINE_CONFIG: The path to the limine configuration file.ISO_NAME: The name of the ISO file to generate.ISO_ROOT: The directory to store ISO files in.
Alright, you hit the point where you don't have to read the rest of this README. Have fun tinkering with the kernel!
I have written this kernel in a kinda circular way. I have a general idea of what I want to implement, but I don't know how to implement it. So, I do the following:
- Do a whole heck of a lot of research about the feature I want to implement.
- Write some kinda poorly written code that does the thing I want to do.
- Work on other parts of the kernel for a while.
- Come back to the code with more knowledge and rewrite it.
I did this heavily for the memory management system, and am currently doing it for context switching and scheduling.
This project originally started as snakian. I was just trying to make a snake operating system, much inspired by jdh's Tetris OS Video. If you look at snakian, I want to warn you that the code is.. not the best.
I quickly in that project realized I wanted to do more than just a snake game (that I never even implemented anyways), so that project shifted to more of a general kernel. By that point though, I had made a lot of mistakes in the codebase, so I decided to start over. That's when I started this project.
This project does a lot differently than snakian. Snakian was almost a word-for-word following of Philipp Oppermann's blog. With nova though, I was confident enough to start off with something quite different. I used limine instead of bootloader, and implemented stuff with a lot more care and abstraction.
This is the longest I have ever worked on a single project (15 months... holy cow), so I am really proud of this project. Yes, all this knowledge is incredibly niche, but I have learned a lot about the inner workings of computers and operating systems.
Also, Honestly, It's really cool to have gotten to this point when I remember at the start seeing so many resources saying to not even attempt it if you don't have 10 years of experience or like a bachelor's degree in computer science. I am a senior in high school with no formal education in computer science, and I have gotten to this point. I am really proud of myself for that.
If your reading this, and you are thinking about starting a project this massive and research-heavy, I say do it.
It wouldn't hurt to try, and you'd at least learn a lot.
- Philipp Oppermann's Blog
- OSDev Wiki
- Heavy on this. So much good information.
- Redox Kernel
- The whole redox project is helpful, but I have found the kernel to be the most helpful.
- EuraliOS
- Small kernel written in rust. Helped me with context switching. (Mainly the assembly stub)