• Stars
    star
    295
  • Rank 140,336 (Top 3 %)
  • Language
    Rust
  • License
    MIT License
  • Created over 8 years ago
  • Updated almost 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A hobbyist microkernel written in Rust, featuring a capability-based system similar to seL4.

Rux, a microkernel written in Rust

Rux is a hobbyist microkernel written in Rust, featuring a capability-based system similar to seL4.

Overview

Rux's goal is to become a safe general-purpose microkernel. It tries to take advantage of Rust's memory model -- ownership and lifetime. While the kernel will be small, unsafe code should be kept minimal. This makes updating functionalities of the kernel hassle-free.

Rux uses a design that is similar to seL4. While there won't be formal verification in the short term, it tries to address some design issues of seL4, for example, capability allocation.

Contributing

We use Phabricator to manage code reviews and collabration. To submit a patch, install arcanist. After you finished coding, run arc diff and it will guide you to create a new patch in Phabricator.

If you don't like arcanist, you can also submit raw diff directly through the web interface. Refer to guide in this page for details on how to do this.

Quickstart

Currently, due to packaging problem, the kernel is only tested to compile and run on Linux with x86_64. Platforms with qemu and compiler target of x86_64 should all be able to run this kernel, but do it at your own risk.

To run the kernel, first install Rust, qemu, and cross-compiled GNU's binutils. The easiest way to do it is through the default.nix file provided in the source code. Install Nix, then go to the source code root and run the following command:

nix-shell

After that, run:

make run

You should see the kernel start to run with a qemu VGA buffer. The buffer, after the kernel successfully booted, should show a simple command-line interface controlled by rinit program launched by the kernel. Several commands can be used to test things out.

echo [message]

Echo messages and print them back to the VGA buffer.

list

Print the current CPool slots into the kernel message buffer.

retype cpool [source slot id] [target slot id]

Retype an Untyped capability into a CPool capability. [source slot id] should be a valid slot index of an Untyped capability. [target slot id] should be an empty slot for holding the retyped CPool capability.

Example: Talk With a Child Task

The rinit program will start the command line interface when it is the first to run. For all subsequent rinit programs, they will wait on a channel (using the root CPool of index 255), and print out the value to the serial buffer.

When you see the command line in the qemu VGA buffer, the "parent" rinit program has been successfully started. We can then create a "child" program using the same memory layout (sharing one page table), and make them talk.

When the "parent" rinit program is started, you should see something like below in the VGA buffer:

Child entry should be at: 0x88b0 (34992)
Child stack pointer should be at: 0x70003ffc (1879064572)

Those messages are useful if we want to create a "child".

To do this, we first retype a new task from an untyped capability.

retype task 2 249

This creates a new task in "inactive" state, which allows us to do further settings. We then set its stack pointer and instruction pointer to the valid value:

set stack 249 1879064572
set instruction 249 34992

Then we set the task's root CPool and top page table the same as the "parent":

set cpool 249 0
set table 249 3

The task buffer is used for system calls, thus we need a new one for the child. Fortunately, in the kernel kmain, we have already created one at index 250, so we can set that as the "child"'s buffer.

set buffer 249 250

After that, we can set the state of the task to active. This will start the task.

set active 249 1

If you are lazy and don't want to create the task from scratch. The command below automates the task from retyping tasks from untyped to activating the task.

start child

After the child has started, we can send numbers to channel (with CPool index 255).

send raw 5

You should see [kernel] Userspace print: Received from master: 5 in the serial message buffer.

You can also send capabilities over the channel. Rux uses different system calls to send raw values, payloads and capabilities through channels. If you wish to send capabilities, modify let value: u64 = system::channel_take(CAddr::from(255)); in child_main of rinit/src/lib.rs to let value: CAddr = system::channel_take_cap(CAddr::from(255));.

send cap 0

And the top-level capability pool capability (CPoolCap) is copied again from parent to child.

Source Code Structure

The development of Rux happen in the master branch in the source code tree. The kernel resides in the kernel folder, with platform-specific code in kernel/src/arch. For the x86_64 platform, the kernel is booted from kernel/src/arch/x86_64/start.S. The assembly code them jumps to the kinit function in kernel/src/arch/x86_64/init/mod.rs.

After the kernel is bootstrapped, it will initialize a user-space program called rinit, which resides in the rinit folder. The user-space program talks with the kernel through system calls, with ABI defined in the package abi, and wrapped in system.

Kernel Design

Capabilities

Capabilities are used in kernel to manage Kernel Objects. Those Capabilities are reference-counted pointers that provide management for object lifecycles.

Capabilities in user-space can be accessed using so-called CAddress, refered through the root capability of the user-space task. This helps to handle all permission managements for the kernel, and thus no priviliged program or account is needed.

Current implemented capabilities are:

  • Untyped memory capability (UntypedCap)
  • Capability pool capability (CPoolCap)
  • Paging capability
    • PML4Cap, PDPTCap, PDCap, PTCap
    • RawPageCap, TaskBufferPageCap
    • VGA buffer
  • CPU time sharing capability (TaskCap)
  • Inter-process communication capability (ChannelCap)

Example: Initialize a New Task

This example shows how to initialize a new task using the capability system.

  • Create an empty TaskCap.
  • Create an empty CPoolCap.
  • Initialize paging capabilities (One PML4Cap, Several PDPTCap, PDCap, PTCap and RawPageCap)
  • Assign the stack pointer in TaskCap.
  • Load the program into those RawPageCap.
  • Assign the PML4Cap to TaskCap.
  • Assign the CPoolCap to TaskCap.
  • Switch to the task!

Implementation

Implementing reference-counted object is a little bit tricky in kernel, as objects need to be immediately freed, and all weak pointers need to be cleared after the last strong pointer goes out. Rux's implementation uses something called WeakPool to implement this. The original reference counted object (called Inner), form a double-linked list into the nodes in multiple WeakPools.

Capability Pools

Capability Pools (or CPool) are used to hold multiple capability together. This is useful for programs to pass around permissions, and is essential for CPool addressing. In implementation, capability pools are implemented as a WeakPool.

Tasks

A task capability has a pointer to a capability pool (the root for CPool addressing), a task buffer (for kernel calls), and a top-level page table. When switching to a task, the kernel switches to the page table specified.

The switch_to function implemented uses several tricks to make it "safe" as in Rust's sense. When an interrupt happens in userspace, the kernel makes it as if the switch_to function has returned.

In kernel-space, interrupts are disabled.

Channels

Tasks communicate with each other through channels. A channel has a short buffer holding messages sent from a task, and will respond this to the first task that calls wait on the channel.

More Repositories

1

reread

Hackable Hacker News Reader.
JavaScript
160
star
2

figure

Fig powered mini-Heroku
Shell
151
star
3

nlp.intent

An open source NLP library for recognizing intent and slots.
Clojure
53
star
4

flow

Simple Flow-based Machine Learning with Tensorflow
CSS
23
star
5

bm

Binary merkle tree implementation.
Rust
15
star
6

sputnikvm-in-browser

Run SputnikVM in browser through wasm32-unknown-emscripten target.
Rust
13
star
7

coinbase_exchange_bot

a trade bot for the coinbase exchange
JavaScript
12
star
8

substrate-pow-template

Rust
11
star
9

HTTP2.jl

A HTTP2 support library for Julia
Julia
11
star
10

sputnikvm-on-rux

SputnikVM for embedded devices.
Rust
8
star
11

rust-hmac-drbg

Pure Rust Implementation of Hmac DRBG
Rust
7
star
12

etclient

A bare minimum Ethereum and Ethereum Classic client built on top of SputnikVM and etcommon.
Rust
6
star
13

blockchain-rs

A generic blockchain interface for Rust
Rust
5
star
14

u256

A bare minimal (and will be fast) U256 implementation for Rust
Rust
4
star
15

asc15-gridding

The Gridding Code for ASC15.
C++
3
star
16

active_logic

Rails Plugin for Convenient Business Logic
Ruby
3
star
17

HPack.jl

Julia's Header Compression for HTTP/2
Julia
3
star
18

ec-rs

Elliptic Curve Cryptography in Pure Rust
Rust
2
star
19

vecarray

Generic array backed by a vector
Rust
2
star
20

substrate-actors-template

Template for working with Substrate's pallet-actors
Rust
2
star
21

stylesheets

Stylesheets used for That World website
CSS
1
star
22

better_exams

CSS
1
star
23

beaty-android

Java
1
star
24

polkamisc

Miscellaneous scripts used for Polkadot validators.
Nix
1
star
25

beaty-server

Ruby
1
star
26

taskibble

Fork of Scribble for task management
Racket
1
star
27

universe.ns.mg

2D Universe Simulation
JavaScript
1
star
28

press

1
star
29

99problems

Noted problems in Ocaml
OCaml
1
star
30

wasmi-on-rux

Run `wasmi` in the Rux microkernel!
Rust
1
star
31

blog.sorpaas.com

Old Blog Site Placeholder for redirection
1
star
32

ahnote

Ah Note is a notebook based on Dillinger
JavaScript
1
star
33

chtholly

Implementation of Chtholly Tree, a data structure used in solutions of CF896C.
Rust
1
star
34

bucme-fetcher

The BUCME Fetcher
Ruby
1
star
35

2train

Ruby
1
star
36

munje

A Time-tensed Logical Programming Language
Haskell
1
star
37

casper-validator

Dummy Casper validator implementation in javascript
JavaScript
1
star