---
# System prepended metadata

title: Rust for Linux 基本介紹，關於 Kbuild

---

# Rust for Linux 基本介紹，關於 Kbuild

### Rust for Linux 簡介
Rust for Linux 為在 Linux Kernel 中使用 Rust 撰寫 Kernel Module 的一個專案

Rust for Linux 現況在 Git 主線上的更新並不多，大部份為升級一些工具的版本 (但是不會升級到最新的版本)，基於 Git 主線的模組開發可以參照 Rust for Linux 在 Lore 中郵件信箱中參考。

在 `rust/macros/lib.rs` 中可以看到我們定義了一個 proc_macro，傳入的參數為 TokenStream
```rust
#[proc_macro]
pub fn module(ts: TokenStream) -> TokenStream {
    module::module(ts)
}
```
這邊傳入的 TokenStream，指的就是 `module!{}` 裡面我們所傳入的資訊，包含 type, name, author 等等，傳入的參數定義如下面所表示
```rust
use kernel::prelude::*;
module!{
     type: MyModule,                           /// #(require)
     name: "my_kernel_module",                 /// #(require)
     author: "Rust for Linux Contributors",
     description: "My very own kernel module!",
     license: "GPL",                           /// #(require)
     params: {
        my_i32: i32 {
            default: 42,
            permissions: 0o000,
            description: "Example of i32",
        },
        writeable_i32: i32 {
            default: 42,
            permissions: 0o644,
            description: "Example of i32",
        },
    },
 }
```


接著會呼叫 `rust/macros/module.rs` 這個檔案中 `module` 函式。
```rust
pub(crate) fn module(ts: TokenStream) -> TokenStream {
    let mut it = ts.into_iter();

    let info = ModuleInfo::parse(&mut it);

    let mut modinfo = ModInfoBuilder::new(info.name.as_ref());
    if let Some(author) = info.author {
        modinfo.emit("author", &author);
    }
    if let Some(description) = info.description {
        modinfo.emit("description", &description);
    }
    modinfo.emit("license", &info.license);
    if let Some(alias) = info.alias {
        modinfo.emit("alias", &alias);
    }
...
```

### Rust for Linux 提供框架
#### 關於 Binding
Rust for Linux 對於 Linux Kernel 中核心基礎建設提供了一個基本的封裝，封裝的部份是基於 Bindings 和 helper 對使用 C 語言撰寫的 Linux Kernel 的界面封裝成 Rust 能夠呼叫的界面。

由於大部分 Linux Kernel 所提供的界面尚未被 Rust for Linux 進行封裝，如果我們要自行添加界面借助 Bindings 機制去產生對應的 Rust 界面，我們可以通過 `binding_helpers.h` 或是 `helper.c` 產生出對應的界面。

:::info
- `binding_helper.h`: 大部分 C 的標頭檔我們都可以使用這個檔案去產生出對應的 Rust 界面，產生的方式為呼叫 Rust Bindgen 這個工具。
- `helper.c`: 對於 C 語言中的巨集定義，我們則會使用這個檔案去產生出對應的 Rust 界面 (但目前情況仍然相當不穩定，如 `le16_to_cpu` 等等)。
:::
#### `Binding_helpers.h` 的使用
以下為 `Binding_helper.h` 內容
```c
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Header that contains the code (mostly headers) for which Rust bindings
 * will be automatically generated by `bindgen`.
 *
 * Sorted alphabetically.
 */

#include <kunit/test.h>
#include <linux/amba/bus.h>
#include <linux/cdev.h>
#include <linux/clk.h>
#include <linux/errname.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/fs_parser.h>
#include <linux/gpio/driver.h>
#include <linux/hw_random.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irqdomain.h>
#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/netfilter_arp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/random.h>
#include <linux/refcount.h>
#include <linux/security.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/uaccess.h>
#include <linux/uio.h>

/* `bindgen` gets confused at certain things. */
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;
const __poll_t BINDINGS_EPOLLIN = EPOLLIN;
const __poll_t BINDINGS_EPOLLOUT = EPOLLOUT;
const __poll_t BINDINGS_EPOLLERR = EPOLLERR;
const __poll_t BINDINGS_EPOLLHUP = EPOLLHUP;

const loff_t BINDINGS_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE;
```
如果我們要加入其他標投檔產生出對應的 Rust 界面，我們可以將 C 標投檔放置到 `include` 底下，如 `include/linux/example/example.h`
```c
...

#include <linux/example/example.h>
    
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;
const __poll_t BINDINGS_EPOLLIN = EPOLLIN;
const __poll_t BINDINGS_EPOLLOUT = EPOLLOUT;
const __poll_t BINDINGS_EPOLLERR = EPOLLERR;
const __poll_t BINDINGS_EPOLLHUP = EPOLLHUP;

const loff_t BINDINGS_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE;
```
接著在編譯時，我們會在編譯輸出中看到我們藉由 bindgen 工具產生出兩個檔案，分別為 `bindgens_generated.rs` 以及 `bindings_helpers_generated.rs` 這兩個檔案，檔案內容即為對應到的 Rust 界面。

以下為 `bindgens_generated.rs` 中部份內容
```rust
#[repr(C)]
#[derive(Copy, Clone)]
pub struct list_head {
    pub next: *mut list_head,
    pub prev: *mut list_head,
}
impl Default for list_head {
    fn default() -> Self {
        unsafe { ::core::mem::zeroed() }
    }
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct hlist_head {
    pub first: *mut hlist_node,
}
impl Default for hlist_head {
    fn default() -> Self {
        unsafe { ::core::mem::zeroed() }
    }
}
...
extern "C" {
    pub fn seq_hlist_start(head: *mut hlist_head, pos: loff_t) -> *mut hlist_node;
}
extern "C" {
    pub fn seq_hlist_start_head(head: *mut hlist_head, pos: loff_t) -> *mut hlist_node;
}
extern "C" {
    pub fn seq_hlist_next(
        v: *mut core::ffi::c_void,
        head: *mut hlist_head,
        ppos: *mut loff_t,
    ) -> *mut hlist_node;
}
```
函式呼叫的部份使用到 FFI 呼叫。

而如果要在我們使用 Rust 撰寫的核心模組中使用 `bindgens_generated.rs` 中對應的界面，則可以加上 `use kernel::bindings::*;` 使用。

使用 `bindgens_generated.rs` 提供的界面，實際上相當於直接呼叫 C 語言所撰寫的函式，而對於這類型的呼叫我們需要以 `unsafe` 進行標注。

