Skip to content
rusthacks.dev
Systems

Rust for Linux Drivers and Out of Tree Kernel Modules

Rust is a supported language in the Linux kernel for new drivers and abstractions. See what that means, the toolchain and kernel configuration, the shape of a module, and why it is not a general Rust-everywhere switch.

3 min read
Summarize in AI
Flat illustration for the post “Rust for Linux Drivers and Out of Tree Kernel Modules”, composed around architecture.

Rust entered the Linux kernel as an experimental second language and became a permanent, supported option for new code, with real drivers shipping in recent releases. The details change with every kernel version, so this post explains the model and the shape of the code, and points at the authoritative documentation for specifics. It is not a tutorial that you can follow without a matching kernel tree; treat the code as illustrative and check it against the version you build.

What changes compared with user-space Rust

  • No std. Kernel Rust uses core and alloc plus the kernel's own kernel crate. There is no std::fs, std::thread or println!; use kernel logging macros such as pr_info!.
  • Allocation can fail. The kernel must not panic on out-of-memory, so allocating APIs return Result, and Box::new becomes KBox::new(value, GFP_KERNEL)? with allocation flags.
  • Panics are a kernel bug. The goal is code that does not panic; errors flow through Result<T, Error> mapping to errno values.
  • Concurrency primitives are the kernel's: spinlocks, mutexes, RCU, with Rust wrappers that tie the data to its lock.
  • A fixed toolchain. Each kernel release names the Rust versions it supports, and uses some unstable compiler features it has negotiated with the Rust project.

Enabling it

Shell
rustup override set <version named in the kernel docs>
rustup component add rust-src rustfmt clippy
cargo install --locked bindgen-cli
make LLVM=1 rustavailable      # reports whether Rust support can be built
make LLVM=1 menuconfig         # General setup -> Rust support

The rustavailable target explains what is missing: compiler version, libclang or the source component. The kernel builds Rust with LLVM and Clang (LLVM=1) for the best-supported path. Then enable CONFIG_RUST and the sample modules (CONFIG_SAMPLES_RUST) to try it.

The shape of a module

rust
// samples/rust/rust_minimal.rs (shape, simplified)
use kernel::prelude::*;

module! {
    type: RustMinimal,
    name: "rust_minimal",
    author: "Example Author",
    description: "Rust minimal sample",
    license: "GPL",
}

struct RustMinimal {
    numbers: KVec<i32>,
}

impl kernel::Module for RustMinimal {
    fn init(_module: &'static ThisModule) -> Result<Self> {
        pr_info!("Rust minimal sample (init)\n");
        let mut numbers = KVec::new();
        numbers.push(72, GFP_KERNEL)?;
        numbers.push(108, GFP_KERNEL)?;
        Ok(RustMinimal { numbers })
    }
}

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

The module! macro generates the entry and exit points and metadata. init returns a Result; Drop is the module's exit. Note the allocation flag and the ? on every push. Names such as KVec and the exact macro fields have changed between kernel versions, so use the sample files in your tree as the reference, not this listing.

Where safety comes from

The kernel crate wraps C structures in Rust types that encode their rules: a locked value can only be accessed through the guard, a reference-counted device is an ARef<Device> that increments and decrements properly, and registration objects unregister on drop. A driver written purely against these abstractions needs little or no unsafe, and a memory-safety bug has to live in the abstraction layer, which is small, heavily reviewed code. That is the entire argument for the project: concentrate the unsafe code and make drivers, the largest source of kernel vulnerabilities, safe by construction (safe wrappers around unsafe).

What it does not mean

  • It is not a rewrite of the kernel; C remains the main language, and Rust is for new code.
  • It does not remove unsafe; the bindings and abstractions use it.
  • It is not a way to run user-space Rust crates in the kernel: the ecosystem of std crates does not apply.
  • APIs and coverage are evolving; code for one release may need changes for the next.

Where to start

  1. Read the kernel's Documentation/rust/ (quick start, coding guidelines, general information).
  2. Build a kernel in a VM with Rust enabled and load the sample modules.
  3. Read an in-tree Rust driver for your area of interest.
  4. If your interest is user-space system programming, system calls with rustix and daemons with systemd are a gentler entry point.
Taggedkernel
Share

Keep reading

Get new posts by email

We send one email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.