A Calustra - Eloy Coto Pereiro Home About Books

Rust Kernel Module Development

on

The other day, I explained how to deploy a sandbox VM to develop Kernel modules written in Rust. Over the past few days, I've been writing some code that has helped me discover how to create a new module written in Rust.

At the moment, support is not complete. The Kernel is the largest monolithic application that you can use, and changes happen in small steps. On the other hand, Rust-for-Linux progresses at a faster pace, so many things are merged there before they land in the rust-next Kernel branch.

The way to use C from the Kernel is by using Bindgen. Bindgen is a tool that generates Rust FFI bindings for C. You can follow these screencasts (0 , 1) from Daniel Stenberg on how Curl uses Hyper (I also recommend his coding screencasts). The main idea is that the tool creates Rust bindings for a header file. Here's a quick example:

// example.h
struct Person {
  char name[50];
  int age;
  float height;
};
// output of bindgen  example.h
//
/* automatically generated by rust-bindgen 0.69.4 */

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Person {
  pub name: [::std::os::raw::c_char; 50usize],
  pub age: ::std::os::raw::c_int,
  pub height: f32,
}

The Bindgen example follows the same path taken by Android , and it's one of the projects that led Google to invest $1M to make it more reliable.

In the Kernel, the generated code will be located at rust/bindings/bindings_generated.rs, and all the header bindings are located in rust/bindings/bindings_helper.h. This is the first pothole that I faced: currently, support for bindings is still limited, so for example, no file_operations or iio bindings are in place yet! You can pick a patch from the rust-in-kernel project, but not natively.

Nevertheless, having generated code can be tricky. The Rust team creates a lot of methods on top of the bindings, such as From functions, Deref, Display, etc.. to make it easier to work in both ways. You can see an example here:

Source

impl AsRef<BStr> for CStr {
    #[inline]
    fn as_ref(&self) -> &BStr {
        self.as_bytes()
    }
}

impl Deref for CStr {
    type Target = BStr;

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.as_bytes()
    }
}

As important as it is to read the C native structs and functions from Rust, it's also important to share the Rust code with C. For example, a module load needs to have some predefined functions, so it's important to use extern "C" in some functions declaration. This also creates a header file to be consumed.

And for sure, one important step! How can I find the available bindings without diving into the source code? There is an available target for the makefile, rustdoc, which creates the Rust documentation. With extended examples, you can follow all available bindings there. For creating and bootstrapping the Kernel documentation I made this simple GitHub action.

As the ecosystem matures and additional bindings and tooling become available, the process of developing Rust modules for the kernel will likely become more straightforward. Currently, the tooling is working, and the Rust team needs to take small steps to make more APIs available. Overall, the path looks great!

Finally, I'm happy to see the first PHY driver as part of the 6.8 kernel :-)

Tags

Related articles: