SkillAgentSearch skills...

Octox

Unix-like OS in Rust inspired by xv6-riscv

Install / Use

/learn @o8vm/Octox
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

#+title: octox #+author: Hayato Ohhashi #+email: o8@vmm.dev

octox is a Unix-like operating system inspired by xv6-riscv. octox loosely follows the structure and style of xv6, but is implemented in pure Rust.

[[https://vhs.charm.sh/vhs-6MQBIyAo3DpBrARBxHxL35.gif]]

  • Everything from kernel, userland, mkfs, to build system is written in safe Rust as much as possible.
  • There are no dependencies on external crates.
  • The userland has a library similar to Rust’s std with K&R malloc.
  • Multi-core support, buddy allocator as kernel-side memory allocator, file system with logging support, etc.
  • Getting Started

** Requirements

  • Install the rust toolchain to have cargo installed by following [[https://www.rust-lang.org/tools/install][this]] guide.
  • Install ~qemu-system-riscv~
  • (option) Install ~gdb-multiarch~

** Build and Run

  • Clone this project & enter: ~git clone ... && cd octox~
  • Build: ~cargo build --target riscv64gc-unknown-none-elf~.
  • Run: ~cargo run --target riscv64gc-unknown-none-elf~, then qemu will boot octox. To exit, press ~Ctrl+a~ and ~x~.

** Play with the Shell

A very simple shell is implemented. In addition to executing commands, you can only do the following things.

  • Pipe: ~cat file | head | grep test~
  • Dump processes: ~Ctrl + P~
  • End of line: ~Ctrl + D~
  • Redirect output: ~>~, ~>>~
  • Development

** Userland Application

The userland comes with a user library called ulib (located at src/user/lib) that is similar to Rust’s std, so you can use it to develop your favorite commands. If you create a bin crate named ~_command~ in src/user/bin, the build.rs and mkfs.rs will place a file named ~command~ in the file system and make it available for use.

  • In src/user/Cargo.toml, define a bin crate with the name of the command you want to create with a ~_~ prefix #+begin_src toml [[bin]] name = "_rm" path = "bin/rm.rs" #+end_src

  • userland is also no_std, so don’t forget to add ~#[no_std]~. Use ulib to develop any command you like. Here is an example of the rm command. #+begin_src rust #![no_std] use ulib::{env, fs};

    fn main() { let mut args = env::args().skip(1).peekable();

      if args.peek().is_none() {
          panic!("Usage: rm files...")
      }
      for arg in args {
          fs::remove_file(arg).unwrap()
      }
    

    } #+end_src

  • Then, ~cargo run --target riscv64gc-unknown-none-elf~ in the root of octox.

  • To use ~Vec~ and ~String~, etc, do the following: #+begin_src rust extern crate alloc; use alloc::{string::String, vec::Vec}; #+end_src

** Kernel

Developing in src/kernel. Here is an example of adding a system call. If you want to add a new system call, you only need to add a definition to the system call table in libkernel, and the userland library will be automatically generated by build.rs.

  • Add a variant and Syscall Number to ~enum SysCalls~ in src/kernel/syscall.rs. Here is ~Dup2~ as an example: #+begin_src rust pub enum SysCalls { Fork = 1, ..., Dup2 = 23, Invalid = 0, } #+end_src
  • Define the function signature of the system call in the ~TABLE~ of ~SysCalls~. Use the enum type ~Fn~ to describe the return type(~U~ (Unit), ~I~ (Integer), ~N~ (never)) and use ~&str~ to represent arguments. then, define kernel-side implementation as a method on ~SysCalls~. ~cfg~ flag is used to control the compilation target for kernel and the rest. Here is an example of ~dup2~: #+begin_src rust impl SysCalls { pub const TABLE: [(fn, &'static str); variant_count::<Self>()] = [ (Fn::N(Self::Invalid), ""), (Fn::I(Self::fork), "()"), (Fn::N(Self::exit), "(xstatus: i32)"), ..., (Fn::I(Self::dup2), "(src: usize, dst: usize)"), ]; pub fn dup2() -> Result<usize> { #[cfg(not(all(target_os = "none", feature = "kernel")))] return Ok(0); #[cfg(all(target_os = "none", feature = "kernel"))] { let p = Cpus::myproc().unwrap().data_mut(); let src_fd = argraw(0); let dst_fd = argraw(1); if src_fd != dst_fd { let mut src = p.ofile.get_mut(src_fd).unwrap() .take().unwrap(); src.clear_cloexec(); p.ofile.get_mut(dst_fd) .ok_or(FileDescriptorTooLarge)?.replace(src); } Ok(dst_fd) } } #+end_src
  • With just these steps, the dup2 system call is implemented in both kernel and userland.
  • License

Licensed under either of

  • [[http://www.apache.org/licenses/LICENSE-2.0][Apache License, Version 2.0]]
  • [[http://opensource.org/licenses/MIT][MIT license]]

at your option.

  • Acknowledgments

octox is inspired by [[https://github.com/mit-pdos/xv6-riscv][xv6-riscv]].

I'm also grateful for the bug reports and discussion about the implementation contributed by Takahiro Itazuri and Kuniyuki Iwashima.

  • Contribution

This is a hobby learning project, but contributions are welcome! However, please note that reviews may take considerable time. Discussions and advice are always appreciated.

Related Skills

View on GitHub
GitHub Stars1.6k
CategoryDevelopment
Updated20h ago
Forks64

Languages

Rust

Security Score

100/100

Audited on Mar 30, 2026

No findings