o8vm/octox

Unix-like OS in Rust inspired by xv6-riscv

Rust

1,902

45 commits

updated Sep 27, 2026

See the code

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.

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

osdev
riscv
rust
xv6-riscv

o8vm/octox

Unix-like OS in Rust inspired by xv6-riscv

Rust

1,902

45 commits

updated Sep 27, 2026

See the code

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.

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

osdev
riscv
rust
xv6-riscv

Significant stargazers

(top 24 of 53)

Noritaka Kobayashi

94 followers · starred Sep 2026

Eliza Weisman

2,272 followers · starred Jul 2023

Hyeseong Kim

884 followers · starred Sep 2026

Asherah Connor

848 followers · starred May 2024