..

Linux Kernel Rust Modules

You can use any editor you want, but remember that vi vi vi is the text editor of the beast.

– Richard Stallman

This post is the second part of the series about Rust in the Linux Kernel. If you haven’t read the part I, please do it before continuing.

My objective here is to show how to build a Linux Kernel module in Rust and general recommendations about the process.

In-Tree Rust Modules

Currently, the Linux Kernel has a few in-tree Rust modules samples in the samples/rust directory. So, you can add a new module directly there.

First, we need to modify the kernel samples/rust/Kconfig file to add a new menu entry for our module. In this example, we will add a new module called hello_rust.

config SAMPLE_HELLO_RUST
	tristate "Rust Hello World module"
	help
	  This option builds the Rust Hello World module.

	  To compile this as a module, choose M here:
	  the module will be called rust_vdev.

	  If unsure, say N.

Also, we need to add a new entry in the samples/rust/Makefile file:

obj-$(CONFIG_SAMPLE_HELLO_RUST) += hello_rust.o

Now, add the module code in the samples/rust/hello_rust.rs file:

// SPDX-License-Identifier: GPL-2.0
//! Rust LKM Template

mod module {
  use kernel::prelude::*;

  pub(crate) fn test() {
      pr_info!("Rust LKM Template (test)\n");
  }
}

use module::test;

use kernel::prelude::*;

module! {
    type: RustLkmTemplate,
    name: "rust_lkm_template",
    author: "Pablo Alessando Santos Hugen",
    description: "Rust KLM Template",
    license: "GPL",
}

struct RustLkmTemplate(&'static str);

impl kernel::Module for RustLkmTemplate {
    fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
        let message: &'static str = "Hello World!";
        test();
        pr_info!("Rust LKM Template (init)\n");
        pr_info!("Am I built-in? {}\n", !cfg!(MODULE));

        Ok(RustLkmTemplate(message))
    }
}

impl Drop for RustLkmTemplate {
    fn drop(&mut self) {
        pr_info!("My message is: {}\n", self.0);
        pr_info!("Rust LKM Template (exit)\n");
    }
}

Finally, we can enable the module using the make LLVM=1 CLIPPY=1 menuconfig command, and set the CONFIG_SAMPLE_HELLO_RUST option:

hello_rust enable

After, build the kernel with the make LLVM=1 CLIPPY=1 -j$(nproc) command, and fire up the VM as shown in [part I]({% post_url 2023-04-02-linux-kernel-rust-dev-environment %}).

In the VM, we can see the module in the dmesg output:

intree_output

Whilst this approach is simple and ok if you are a core kernel maintainer, it has some drawbacks otherwise:

  • The module is built in-tree, so it will be built every time we build the kernel.
  • You have to open a huge codebase like the Linux Kernel to add a new module. It can slow down some editors and make the development process harder.

Out-of-Tree Rust Modules

A better approach is to build the module out-of-tree. This way, we can build the module separately from the kernel, having a faster and smooth development process.

For building modules in this way, we use the Kbuild system. The Kbuild system is a set of tools that are used to build the Linux Kernel, and we could use it to build external modules too.

The Kbuild config for a external module is very straightforward, we just need to point the objects in the Kbuild file and use a simple Makefile to build the module. You can refer to the Kbuild external modules docs for more info.

For example, in my simple external module template, one can find a complete example of a Kbuild config for a external module ith additional features like hot-reload in a Virtual Machine:

c_lkm

Ok, but what about the Rust part?

Rust for Linux folks have done an amazing job to make the Rust integration with the Kbuild system seamless.

In the same way that we configure an Kbuild file for a C module, we can do the same for a Rust module. The only difference is that objects are compiled from Rust sources!!! How cool is that?

We can find an example of a external module in the Rust for Linux GitHub Organization. This is a simple template for a external module in Rust.

Also, I’ve created a Rust LKM template based on the Rust for Linux one, with some additional features like hot-reload in a Virtual Machine. In the next session we’re gonna explore it a little bit an see some examples of what we can do with It.

Building and running a Rust LKM

The Rust LKM template is a simple template for a smooth development process of a external module in Rust.

Remember from [part I]({% post_url 2023-04-02-linux-kernel-rust-dev-environment %}) that we have a VM running with the kernel built from the Rust for Linux fork. We can use this VM to develop and test Rust modules smoothly.

The template has a Makefile and a Kbuild file, which are used to build the module and some helper scripts in the assets/ folder:

rust_lkm

The goal of this template is to incrementally add features to it and copy the generated .ko in the VM for testing And not panicking your own kernel.

  • ./scrips/setup This script basically setups the environment for the VM (refer to [part I]({% post_url 2023-04-02-linux-kernel-rust-dev-environment %}) for more info).

  • ./scripts/build, ./scripts/build -t clean, ./scripts/build -t rust-analyzer This script “forwards” targets to the Makefile (build, clean, etc):

    build

  • ./scripts/run This script copies the generated .ko file to the busybox dir, generate an updated initramfs and boot the VM:

    run

  • ./scripts/hot-reload This script builds the module and run every time a change is detected in the source code:

The hot-reload feature is very useful for kernel development, for it allows us to test our changes in the VM without the need to rebuild the kernel every time.

Further Tests

Now that you have a basic understanding of how to build and run a Rust LKM, let’s see some basic examples of what we can do with it:

  • Parrot Module: A silly example of how one can use the procfs subsystem to create a simple file in /proc that returns a string when read.
  • Scull Module: Another example of how to use the procfs subsystem to create a simple file in /proc that returns a persistent string when read.

Also, I strongly recommend you to check out the Rust for Linux documentation, which has a lot of useful information about the Rust integration with the Linux Kernel, for it could help you to learn more about rust abstractions and how to use them in the kernel.

References and Further Reading