# 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` 進行標注。