Skip to main content

[Rust Driver] Let's try build example rust linux driver.

·4 mins
Linux Rust Linux ARMv8A Rust Driver English_Article
Jinwoo Park
Author
Jinwoo Park
Working as a Rust Backend Engineer and previously worked as an Embedded Systems Engineer.

In October, Rust for linux is under the linux-next, not stable
Thus this article would be out-of-date before Linux 6.1 stable comes.

modules, out-of-tree #

There are two main ways to develop kernel modules. In-Of-Tree and Out-Of-Tree. In this article, we’re going to make the Out-Of-Tree method a Rust kernel module.

Before we start #

Check your kernel has been compiled with CONFIG_RUST=y. #

Check with following command.

zcat /proc/config.gz | grep -i CONFIG_RUST=y

The result comes with CONFIG_RUST=y.

But you may not check from /proc/config.gz when using distibution kernel image that downloaded or pre-installed.

Need some build & install rust support kernel see here. See details ⇀

Prepare $KDIR #

$KDIR is path of kernel source.
In this article path of kernel source that system used for boot with CONFIG_RUST.

KDIR and other kernel module descriptions See details ⇀

In my case it’s ~/Develop/linux

# /home/pmnxis/Develop/linux
export KDIR=$HOME/Develop/linux

Looking in to code #

Let’s preview the code rust_out_of_tree.rs

License and imports #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// SPDX-License-Identifier: GPL-2.0

//! Rust out-of-tree sample

use kernel::prelude::*;

module! {
    type: RustOutOfTree,
    name: "rust_out_of_tree",
    author: "Rust for Linux Contributors",
    description: "Rust out-of-tree sample",
    license: "GPL",
}

Lines 1~3, show file’s license information. If you are write the code in company, SomeCompanyName instead GPL-2.0 or just keep GPL-2.0.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// SPDX-License-Identifier: GPL-2.0

//! Rust out-of-tree sample

use kernel::prelude::*;

module! {
    type: RustOutOfTree,
    name: "rust_out_of_tree",
    author: "Rust for Linux Contributors",
    description: "Rust out-of-tree sample",
    license: "GPL",
}

Line 5 means, bring rust for linux library for this code.

In following example module written in C were include like this.

2
3
4
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/irq_work.h>



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// SPDX-License-Identifier: GPL-2.0

//! Rust out-of-tree sample

use kernel::prelude::*;

module! {
    type: RustOutOfTree,
    name: "rust_out_of_tree",
    author: "Rust for Linux Contributors",
    description: "Rust out-of-tree sample",
    license: "GPL",
}

Line 8, implement of the module trait.
Line 9, name of the module, if we written c, it’s the name of *.ko name field. Line 10~12, those fields are simillar with below the example written in c. Those fields are same purpose.

56
57
58
MODULE_AUTHOR("Steven Rostedt");
MODULE_DESCRIPTION("trace-printk");
MODULE_LICENSE("GPL");

We preview macro_rule! module shortly. You can see detail here.

Details for module! See details ⇀

Actual implements #

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct RustOutOfTree {
    numbers: Vec<i32>,
}

impl kernel::Module for RustOutOfTree {
    fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
        pr_info!("Rust out-of-tree sample (init)\n");

        let mut numbers = Vec::new();
        numbers.try_push(72)?;
        numbers.try_push(108)?;
        numbers.try_push(200)?;

        Ok(RustOutOfTree { numbers })
    }
}

impl Drop for RustOutOfTree {
    fn drop(&mut self) {
        pr_info!("My numbers are {:?}\n", self.numbers);
        pr_info!("Rust out-of-tree sample (exit)\n");
    }
}

I just guess working as …

  1. On init (insmod?), print out somewhere with text Rust out-of-tree sample (init)
  2. vec<i32>[72, 108, 200] is stored some kernel memory space with struct RustOutOfTree.
  3. When drop the module (rmmod?), will print out with text [72, 108, 200].

By the way, we need to keep on eyes here.

23
24
        let mut numbers = Vec::new();
        numbers.try_push(72)?;

In line 24, try_push is not exsting in std::Vec. In rust kernel programming, need to use try_push instead std::Vec::push.

Details for alloc::vec::Vec See details ⇀

Also there's some `init` and `drop` functions in line 20 and 33. The code covers those function with `impl for` pattern.
Details for Implementation in rust See details ⇀

I will explain about implementation and it’s philosophy later article.

Run code #

Build it #

make LLVM=1

My rust acceptable kernel build were buiten with LLVM.
So I compile the kernel module with LLVM.

Install module #

sudo insmod ./rust_out_of_tree.ko

After compile, we can there’s rust_out_of_tree.ko inside of project directory.
We can install module with insmod that normally used before.

Inspect result #

# do `sudo rmmod rust_out_of_tree` if you already install the module`

# clear all of dmesg log 
sudo dmesg -C

# install the module
sudo insmod ./rust_out_of_tree.ko

# see log
dmesg

# uninstall the module
sudo rmmod rust_out_of_tree

# check log again.
dmesg

We can check the inspect actual result with above commands.

Insepction Result
As we guess it prints with [72, 108, 200].


Conclusion #

We can summary from this simple kernel module.

Summary #

  • Need to use use kernel::prelude::*; on top of code.
  • module! macro to define some description and board my own struct to the kernel module.
  • kernel::Module templete functions …. -WIP-
  • In kernel programming, use alloc::vec::Vec instead std::Vec.
  • pr_info is just same as way to write with C.

Reference #

  1. https://github.com/Rust-for-Linux/rust-out-of-tree-module
  2. https://www.kernel.org/doc/html/latest/kbuild/modules.html
  3. https://github.com/Rust-for-Linux/linux
  4. https://rust-for-linux.github.io/docs/kernel/prelude/index.html
  5. https://rust-for-linux.github.io/docs/kernel/prelude/macro.module.html
  6. https://rust-for-linux.github.io/docs/kernel/prelude/struct.Vec.html