Thanks to visit codestin.com
Credit goes to github.com

Skip to content

quackitsquinn/novos

Repository files navigation

novos: A little kernel written in rust. (so I can learn osdev)

Running the kernel

To run the kernel, you need to have the following dependencies installed:

  • qemu
  • xorriso
  • rustc and cargo
    • You need to have the x86_64-unknown-none target 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

Then, you can run the kernel with the following command from the root of the repository:

cargo run

This will build the kernel, generate an image, and run it in QEMU.

Project structure

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 by kserial commands.
  • tools: Various python scripts used for debugging. Both scripts are currently old and unused.

SmartCheck

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.

Debugging the kernel

Almost all debugging is done through setting various environment variables that control krun and QEMU behavior.

GDB Auto launching

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"

Runtime options

  • 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_64 will be used.
  • SMP_CORES
    • Controls the number of CPU cores to allocate to QEMU.

DEBUG

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.

QEMU_DEBUG_OPTS

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:

Build-time options

  • 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!

How I have developed this project

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:

  1. Do a whole heck of a lot of research about the feature I want to implement.
  2. Write some kinda poorly written code that does the thing I want to do.
  3. Work on other parts of the kernel for a while.
  4. 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.

History

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.

Personal note

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.

Amazing resources

  • 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)

About

An x86_64 kernel written in rust

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages