├── README.md
├── blk-mq.md
├── docs
├── Rust for Linux 开发技术分享.pdf
├── Rust for Linux 驱动模块开发.pdf
├── readme.md
└── 网卡框架.pdf
├── exercise
├── exercise1.md
├── exercise2.md
├── exercise3.md
└── exercise4.md
├── initrd.md
├── net.md
├── plan.md
└── 优秀文档参考
├── PuzzleFS-下一代Linux容器文件系统之源码分析报告-邝劲强.md
├── Rust_for_Linux驱动模块开发报告-周睿.md
├── Rust_for_Linux驱动模块开发报告-杨凯.md
├── Rust_for_Linux驱动模块开发报告-江昊.md
├── Rust_for_Linux驱动模块开发报告-童浩轩.md
├── Rust_for_Linux驱动模块开发报告-邝劲强.md
├── Rust实现内存块ramdisk设备驱动-江昊.md
├── lcx-Rust-for-Linux-Virtio驱动模块开发.md
├── lcx-Rust-for-Linux-net-driver.md
├── nnull-block-driver分析-童浩轩.md
├── readme.md
└── ye-junzhe-叶钧喆-Rust_for_Linux驱动模块开发.md
/README.md:
--------------------------------------------------------------------------------
1 | # Rust for Linux 驱动模块开发
2 |
3 | ## 课题目标
4 | 学生选修该课题,可以学习到如何一步一步地开发一个Rust for Linux内核驱动;逐步积累并形成学习开发文档;进而有机会为rcore-os Rust操作系统社区以及Linux社区的发展,通过编写Rust驱动等方式贡献一份自己的力量。
5 |
6 | 具体[课题计划](plan.md)
7 |
8 | ## 相关资料参考
9 | Rust for Linux社区仓库: https://github.com/Rust-for-Linux
10 |
11 | Linux之Rust api接口适配e1000网卡驱动: https://github.com/fujita/linux.git
12 |
Rust e1000网卡驱动模块,支持了`e1000`与`e1000e`网卡: https://github.com/elliott10/e1000-driver.git
13 |
14 | Rust实现的VirtIO驱动: https://github.com/rcore-os/virtio-drivers.git
15 |
16 | RISCV物理开发板平台:华山派: https://github.com/sophgo/sophpi-huashan.git
17 |
18 | 内核编译blog参考:https://github.com/rcore-os/blog/blob/master/source/_posts/%E7%BC%96%E8%AF%91Linux%E5%86%85%E6%A0%B8%E5%BC%80%E5%90%AFRust%E8%AF%AD%E8%A8%80%E6%94%AF%E6%8C%81.md
19 |
20 | ## Rust for Linux 设备驱动实现案例
21 |
22 | ### Linux上的Rust驱动程序
23 |
24 | * Western Digital 首席工程师 Andreas Hindborg 在2022 Linux Plumbers Summit上
25 | 展示了用户可以使用 Rust 编写一流的驱动程序,即适用于 Linux 的 SSD NVM-Express (NVMe) 驱动程序。
26 | Linux (PCI) NVMe driver in Rust
27 | https://lpc.events/event/16/contributions/1180/attachments/1017/1961/deck.pdf
28 | https://github.com/metaspace/linux/commits/nvme
29 |
30 | * Asahi Lina Linux组织开发的Apple's M1 GPU驱动
31 | https://github.com/AsahiLinux/linux/commits/gpu/rust-wip
32 |
33 | * 对async异步内核编程的支持,当前状态已经用于允许异步网络TCP连接支持;
34 | https://github.com/Rust-for-Linux/linux/commit/08358e5a955f662daf154a89f3ba43a3f18228a2
35 |
36 | * Google Android 团队正试验移植Rust Binder IPC 驱动,其它 Android 厂商也表示有兴趣开发 Rust 驱动
37 | https://github.com/Rust-for-Linux/linux/commit/d8dcaf80c9535056ea541d8dd876edad52c538a9
38 |
39 | * 9p fs server 方便地从虚拟主机与Host机进行文件系统访问
40 | https://kangrejos.com/Async%20Rust%20and%209p%20server.pdf
41 | https://github.com/wedsonaf/linux/commits/9p
42 |
43 | * Samsung - Null块驱动:Null Block Driver
44 | https://github.com/metaspace/linux/tree/null_block-RFC
45 |
46 | * Cisco - 容器文件系统: PuzzleFS
47 | https://github.com/project-machine/puzzlefs
48 |
49 | * Microsoft TarFS
50 | https://github.com/wedsonaf/linux/commits/vfs
51 |
52 | * Collabora - Video Codec
53 | https://lore.kernel.org/rust-for-linux/20230406215615.122099-1-daniel.almeida@collabora.com/
54 |
--------------------------------------------------------------------------------
/blk-mq.md:
--------------------------------------------------------------------------------
1 | # Linux blk-mq介绍
2 |
3 | Linux blk-mq是Linux块层的一个新框架,它可以让高性能的存储设备通过同时向块设备发送和处理IO请求,从而实现高达数百万的每秒输入/输出操作(IOPS),利用了现代存储设备的并行性。
4 |
5 | ## blk-mq的主要接口
6 |
7 | blk-mq的主要接口有以下几个:
8 |
9 | - `blk_mq_alloc_tag_set`:分配一个tag set结构,用于存储blk-mq的配置信息,例如硬件队列的数量,软件队列的数量,请求的大小,驱动程序的回调函数等。
10 | - `blk_mq_init_queue`:根据tag set结构,初始化一个请求队列(request queue),用于管理块设备的IO请求。
11 | - `blk_mq_alloc_disk`:根据请求队列,分配一个磁盘结构(gendisk),用于表示块设备的基本信息,例如设备号,扇区大小,容量等。
12 | - `blk_mq_free_tag_set`:释放一个tag set结构,以及与之关联的资源,例如tag位图,软件队列,硬件队列等。
13 | - `blk_mq_free_disk`:释放一个磁盘结构,以及与之关联的资源,例如请求队列,设备节点等。
14 | - `blk_mq_start_request`:开始处理一个IO请求,将其从软件队列移动到硬件队列,或者直接发送到硬件。
15 | - `blk_mq_complete_request`:完成一个IO请求,将其从硬件队列移除,或者直接返回给用户空间,同时释放tag和资源。
16 | - `blk_mq_map_queue`:根据CPU编号,返回一个对应的硬件队列,用于将IO请求发送到合适的硬件队列。
17 | - `blk_mq_tag_to_rq`:根据tag编号,返回一个对应的IO请求,用于在硬件队列中查找或操作IO请求。
18 |
19 | ## blk-mq的代码示例
20 |
21 | 下面是一个使用blk-mq的简单的代码示例,它实现了一个虚拟的块设备,可以在内存中读写数据。
22 |
23 | ```c
24 | #include
25 | #include
26 | #include
27 |
28 | #define VBLK_NAME "vblk"
29 | #define VBLK_SECTOR_SIZE 512
30 | #define VBLK_CAPACITY (1024 * 1024 * 1024) // 1 GB
31 | #define VBLK_MAX_REQUESTS 256
32 | #define VBLK_HW_QUEUES 4
33 | #define VBLK_SW_QUEUES 8
34 |
35 | static struct vblk_device {
36 | struct blk_mq_tag_set tag_set;
37 | struct request_queue *queue;
38 | struct gendisk *disk;
39 | u8 *data;
40 | } vblk_dev;
41 |
42 | static int vblk_open(struct block_device *bdev, fmode_t mode)
43 | {
44 | return 0;
45 | }
46 |
47 | static void vblk_release(struct gendisk *disk, fmode_t mode)
48 | {
49 | }
50 |
51 | static const struct block_device_operations vblk_fops = {
52 | .owner = THIS_MODULE,
53 | .open = vblk_open,
54 | .release = vblk_release,
55 | };
56 |
57 | static blk_status_t vblk_queue_rq(struct blk_mq_hw_ctx *hctx,
58 | const struct blk_mq_queue_data *bd)
59 | {
60 | struct request *rq = bd->rq;
61 | struct bio_vec bvec;
62 | struct req_iterator iter;
63 | u64 offset;
64 | u32 len;
65 | u8 *buf;
66 |
67 | blk_mq_start_request(rq);
68 |
69 | offset = blk_rq_pos(rq) * VBLK_SECTOR_SIZE;
70 |
71 | // rq_for_each_segment(bvec, rq, iter) {
72 | // len = bvec.bv_len;
73 | // buf = page_address(bvec.bv_page) + bvec.bv_offset;
74 | // if (rq_data_dir(rq) == READ) {
75 | // memcpy(buf, vblk_dev.data + offset, len);
76 | // } else {
77 | // memcpy(vblk_dev.data + offset, buf, len);
78 | // }
79 | // offset += len;
80 | // }
81 |
82 | blk_mq_complete_request(rq);
83 |
84 | return BLK_STS_OK;
85 | }
86 |
87 | // 定义一个自定义的数据结构,用于存储请求相关的信息
88 | struct vblk_request {
89 | struct request *rq; // 指向blk-mq的请求结构
90 | void *data; // 指向自定义的数据缓冲区
91 | // 其他字段
92 | };
93 |
94 | // 实现.complete接口的函数
95 | static void vblk_complete(struct request *rq, blk_status_t status)
96 | {
97 | // 获取自定义的数据结构
98 | struct vblk_request *vreq = blk_mq_rq_to_pdu(rq);
99 |
100 | // 释放自定义的数据缓冲区
101 | kfree(vreq->data);
102 |
103 | // 调用blk-mq的默认完成函数
104 | blk_mq_end_request(rq, status);
105 | }
106 |
107 | static struct blk_mq_ops vblk_mq_ops = {
108 | .queue_rq = vblk_queue_rq,
109 | .complete = vblk_complete,
110 | };
111 |
112 | static int __init vblk_init(void)
113 | {
114 | int ret;
115 |
116 | // allocate tag set
117 | vblk_dev.tag_set.ops = &vblk_mq_ops;
118 | vblk_dev.tag_set.nr_hw_queues = VBLK_HW_QUEUES;
119 | vblk_dev.tag_set.queue_depth = VBLK_MAX_REQUESTS;
120 | vblk_dev.tag_set.numa_node = NUMA_NO_NODE;
121 | vblk_dev.tag_set.cmd_size = 0;
122 | vblk_dev.tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
123 | vblk_dev.tag_set.driver_data = &vblk_dev;
124 |
125 | ret = blk_mq_alloc_tag_set(&vblk_dev.tag_set);
126 | if (ret) {
127 | pr_err("Failed to allocate tag set\n");
128 | return ret;
129 | }
130 |
131 | // allocate request queue
132 | vblk_dev.queue = blk_mq_init_queue(&vblk_dev.tag_set);
133 | if (IS_ERR(vblk_dev.queue)) {
134 | pr_err("Failed to allocate request queue\n");
135 | ret = PTR_ERR(vblk_dev.queue);
136 | goto free_tag_set;
137 | }
138 |
139 | // allocate disk
140 | vblk_dev.disk = blk_mq_alloc_disk(&vblk_dev.tag_set, NULL);
141 | if (IS_ERR(vblk_dev.disk)) {
142 | pr_err("Failed to allocate disk\n");
143 | ret = PTR_ERR(vblk_dev.disk);
144 | goto free_queue;
145 | }
146 |
147 | // set disk parameters
148 | vblk_dev.disk->major = 0;
149 | vblk_dev.disk->first_minor = 0;
150 | vblk_dev.disk->fops = &vblk_fops;
151 | vblk_dev.disk->private_data = &vblk_dev;
152 | vblk_dev.disk->queue = vblk_dev.queue;
153 | vblk_dev.disk->flags = GENHD_FL_EXT_DEVT;
154 | sprintf(vblk_dev.disk->disk_name, VBLK_NAME);
155 | set_capacity(vblk_dev.disk, VBLK_CAPACITY / VBLK_SECTOR_SIZE);
156 |
157 | // allocate data buffer
158 | vblk_dev.data = vzalloc(VBLK_CAPACITY);
159 | if (!vblk_dev.data) {
160 | pr_err("Failed to allocate data buffer\n");
161 | ret = -ENOMEM;
162 | goto free_disk;
163 | }
164 |
165 | // add disk
166 | add_disk(vblk_dev.disk);
167 |
168 | pr_info("vblk device initialized\n");
169 |
170 | return 0;
171 |
172 | free_disk:
173 | blk_mq_free_disk(vblk_dev.disk);
174 | free_queue:
175 | blk_cleanup_queue(vblk_dev.queue);
176 | free_tag_set:
177 | blk_mq_free_tag_set(&vblk_dev.tag_set);
178 | return ret;
179 | }
180 |
181 | static void __exit vblk_exit(void)
182 | {
183 | // remove disk
184 | del_gendisk(vblk_dev.disk);
185 |
186 | // free data buffer
187 | vfree(vblk_dev.data);
188 |
189 | // free disk
190 | blk_mq_free_disk(vblk_dev.disk);
191 |
192 | // free request queue
193 | blk_cleanup_queue(vblk_dev.queue);
194 |
195 | // free tag set
196 | blk_mq_free_tag_set(&vblk_dev.tag_set);
197 |
198 | pr_info("vblk device exited\n");
199 | }
200 |
201 | module_init(vblk_init);
202 | module_exit(vblk_exit);
203 |
204 | MODULE_LICENSE("GPL");
205 | MODULE_AUTHOR("AAA");
206 | MODULE_DESCRIPTION("A simple example of using blk-mq");
207 | ```
208 |
209 | ## blk-mq的示意图
--------------------------------------------------------------------------------
/docs/Rust for Linux 开发技术分享.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rcore-os/rust-for-linux/a5b3a3cba141e07f4ca57b417380d6a5eb5767d9/docs/Rust for Linux 开发技术分享.pdf
--------------------------------------------------------------------------------
/docs/Rust for Linux 驱动模块开发.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rcore-os/rust-for-linux/a5b3a3cba141e07f4ca57b417380d6a5eb5767d9/docs/Rust for Linux 驱动模块开发.pdf
--------------------------------------------------------------------------------
/docs/readme.md:
--------------------------------------------------------------------------------
1 | ## PPT docs
2 |
--------------------------------------------------------------------------------
/docs/网卡框架.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rcore-os/rust-for-linux/a5b3a3cba141e07f4ca57b417380d6a5eb5767d9/docs/网卡框架.pdf
--------------------------------------------------------------------------------
/exercise/exercise1.md:
--------------------------------------------------------------------------------
1 | # Exercise 1
2 | ## 练习1: Rust for Linux 仓库源码获取,编译环境部署,及Rust内核编译。之后尝试在模拟器Qemu上运行起来。
3 |
4 | 1. Get source code
5 | we have collected all the source code for you in the homework repository(as mentioned before). However, you can also get source code from Rust for Linux official repository:
6 | ```
7 | git clone https://github.com/Rust-for-Linux/linux -b rust-dev
8 | ```
9 | OR the rust version source code by fujita from the following repository:
10 | ```
11 | git clone https://github.com/fujita/linux.git -b rust-e1000
12 | ```
13 | Enjoy it!
14 |
15 | 2. Preparation
16 | First you need to install some packages(if the warning or error tell you need more packages, just intall them all):
17 | ```
18 | sudo apt-get -y install \
19 | binutils build-essential libtool texinfo \
20 | gzip zip unzip patchutils curl git \
21 | make cmake ninja-build automake bison flex gperf \
22 | grep sed gawk bc \
23 | zlib1g-dev libexpat1-dev libmpc-dev \
24 | libglib2.0-dev libfdt-dev libpixman-1-dev libelf-dev libssl-dev
25 | ```
26 |
27 | Besides a Rust environment is necessary, but I think you have done this in stage1.
28 | And install the LLVM:
29 | ```
30 | apt-get install clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang
31 | ```
32 |
33 | Then try to make the kernel support the Rust language:
34 | ```
35 | cd linux
36 | sudo apt install clang llvm
37 |
38 | make LLVM=1 rustavailable
39 | rustup override set $(scripts/min-tool-version.sh rustc)
40 | rustup component add rust-src
41 | cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli
42 | ```
43 |
44 | 3. Build the kernel tree and the image
45 | As we introduced in our lesson, you have to
46 | ```
47 | make ARCH=arm64 LLVM=1 O=build defconfig
48 |
49 | make ARCH=arm64 LLVM=1 O=build menuconfig
50 | #set the following config to yes
51 | General setup
52 | ---> [*] Rust support
53 |
54 | make ARCH=arm64 LLVM=1 -j8
55 | ```
56 | After that, under your linux folder you can find a file named vmlinux, which means your building is success. Congratulations!
57 |
58 | Remember, when you building the kernel, some errors might happen, which is very normal and how to fix the bug or just ignore it, is a part of your homework.
59 | For example, some modules might show an error called "modpost \"xxx\" [xxx.ko] underfined!" on my assistant's environment, but that's just an error of modules and have no affection on the kernel image, so just ignore it.
60 | If you have any errors during the process of building the kernel tree, we do not restrict you to use any method to solve the problem, including using google, querying stackoverflow, asking chatgpt, or directly consulting the teaching assistant, etc.
61 | Testcase and Grade
62 | This part accounting for 60% of the score.
63 |
64 | No testcase.
65 | We have no way to test whether you have made a correct environment and built your kernel tree and image successfully on your computer, so you have to submit a report and show the runtime screenshot in your report.
66 | If errors occur during configuration, we want you to be able to document them. If it doesn't work out in any way, you can get 60 points for reporting your mistake.
67 |
--------------------------------------------------------------------------------
/exercise/exercise2.md:
--------------------------------------------------------------------------------
1 | # Exercise 2
2 |
3 | ## 练习2: 自定义编写Rust内核驱动模块
4 |
5 | We will only do in-tree compilation. In this part, we need to build a minimal kernel module
6 |
7 | 1. Goto samples/rust directory in Linux folder
8 | 2. Add a rust_helloworld.rs in this directory
9 | 3. Add these content to `rust_helloworld.rs`
10 |
11 | ```
12 | // SPDX-License-Identifier: GPL-2.0
13 | //! Rust minimal sample.
14 |
15 | use kernel::prelude::*;
16 |
17 | module! {
18 | type: RustHelloWorld,
19 | name: "rust_helloworld",
20 | author: "whocare",
21 | description: "hello world module in rust",
22 | license: "GPL",
23 | }
24 |
25 | struct RustHelloWorld {}
26 |
27 | impl kernel::Module for RustHelloWorld {
28 | fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result {
29 | pr_info!("Hello World from Rust module");
30 | Ok(RustHelloWorld {})
31 | }
32 | }
33 | ```
34 | 4. Edit Makefile and Kconfig within samples/rust directory
35 |
36 | ```
37 | obj-$(CONFIG_SAMPLE_RUST_FS) += rust_fs.o
38 | obj-$(CONFIG_SAMPLE_RUST_SELFTESTS) += rust_selftests.o
39 | // ++++++++++++++++ add here
40 | obj-$(CONFIG_SAMPLE_RUST_HELLOWORLD) += rust_helloworld.o
41 | // ++++++++++++++++
42 | subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
43 |
44 | config SAMPLE_RUST_MINIMAL
45 | tristate "Minimal"
46 | help
47 | This option builds the Rust minimal module sample.
48 |
49 | To compile this as a module, choose M here:
50 | the module will be called rust_minimal.
51 |
52 | If unsure, say N.
53 | // +++++++++++++++ add here
54 | config SAMPLE_RUST_HELLOWORLD
55 | tristate "Print Helloworld in Rust"
56 | help
57 | This option builds the Rust HelloWorld module sample.
58 |
59 | To compile this as a module, choose M here:
60 | the module will be called rust_helloworld.
61 |
62 | If unsure, say N.
63 | // +++++++++++++++
64 | config SAMPLE_RUST_PRINT
65 | tristate "Printing macros"
66 | help
67 | This option builds the Rust printing macros sample.
68 |
69 | To compile this as a module, choose M here:
70 | the module will be called rust_print.
71 |
72 | If unsure, say N.
73 | ```
74 |
75 | 5. run make LLVM=1 menuconfig
76 | 6. You can enter `/` and search for the location of the module you want to find. Now make your module compile within kernel binary.
77 | ```
78 | Kernel hacking
79 | ---> Sample Kernel code
80 | ---> Rust samples
81 | ---> <*>Print Helloworld in Rust (NEW)
82 | ```
83 | Remember that in the previous option you need to press Y, in the last option you need to press M.
84 | Then exit menuconfig and save your change.
85 |
86 | 7. Compile kernel again make LLVM=1 and you should see no Error if your previous steps are right.
87 |
88 | Testcase and Grade
89 | This part accounting for 10% of the score.
90 |
91 | If everything is ok, you can see a rust_helloworld.ko in the samples/rust directory.
92 | Then copy it to the src_e1000/rootfs and then run build_image.sh
93 |
94 | After you enter the Linux shell, use 'ls' command, you can find the rust_helloworld.ko.
95 | Then use insmod to start this module.
96 | Finally, you can see the "Hello World from Rust module"
97 |
98 | ### A Small TTY Driver from <>
99 |
100 |
--------------------------------------------------------------------------------
/exercise/exercise3.md:
--------------------------------------------------------------------------------
1 | # 基于Qemu模拟器上的e1000网卡驱动框架,填充驱动初始化函数
2 | 获取e1000-driver代码 https://github.com/yuoo655/e1000-driver
3 |
4 | 相应的linux仓库 https://github.com/fujita/linux/tree/rust-e1000
5 |
6 | 完成Exercise3 Checkpoint 1-5
7 |
8 | # 自定义一个linux内核函数, 并在rust模块中调用它.
9 |
10 | # step1
11 | 在linux include目录下创建一个头文件xx.h, 在里面写好自定义的一个函数, 打印一行log出来.
12 |
13 | # step2
14 | 在linux rust/bindings/bindings_helper.h中把step1中的头文件引用进来. 在rust/helpers.c中参考其中已有例子,生成rust_helper_为前缀的函数.
15 |
16 | # step3
17 | 在Rust网卡驱动模块中模块中引入kernel::bindings::*; 通过bindings::函数名方式调用.
18 |
19 | 作业解答之e1000驱动完整实现:
20 | Rust e1000网卡驱动模块: https://github.com/elliott10/e1000-driver.git
21 |
--------------------------------------------------------------------------------
/exercise/exercise4.md:
--------------------------------------------------------------------------------
1 | # 完成e1000网卡收包和发包函数
2 | 完成Exercise4 Checkpoint 1-2
3 |
4 | [提示](./net.md)
--------------------------------------------------------------------------------
/initrd.md:
--------------------------------------------------------------------------------
1 | 使用 busybox 制作内存文件系统 initramfs:
2 |
3 | 1. 下载解压 busybox 并配置环境变量
4 |
5 | ```shell
6 | wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2
7 | tar -xf busybox-1.35.0.tar.bz2
8 | cd busybox-1.35.0
9 | # 配置环境变量
10 | export ARCH=arm64
11 | export CROSS_COMPILE=aarch64-linux-gnu-
12 | ```
13 |
14 | 2. 配置编译内核的参数
15 |
16 | ```shell
17 | # busybox-1.35.0目录下
18 | make menuconfig
19 | # 修改配置,选中如下项目,静态编译
20 | # Settings -> Build Options -> [*] Build static binary (no share libs)
21 |
22 |
23 | 3. 编译
24 |
25 | ```shell
26 | make -j `nproc`
27 | ```
28 |
29 | 4. 安装
30 |
31 | 安装前 busybox-1.35.0 目录下的文件如下图所示
32 |
33 |
34 |
35 | 输入如下命令
36 |
37 | ```shell
38 | make install
39 | ```
40 |
41 |
42 | 安装后目录下生成了\_install 目录
43 |
44 | 5. 在\_install 目录下创建后续所需的文件和目录
45 |
46 | ```shell
47 | cd _install
48 | mkdir proc sys dev tmp
49 | touch init
50 | chmod +x init
51 | ```
52 |
53 | 6. 用任意的文本编辑器编辑 init 文件内容如下
54 |
55 | ```shell
56 | #!/bin/sh
57 |
58 | # 挂载一些必要的文件系统
59 | mount -t proc none /proc
60 | mount -t sysfs none /sys
61 | mount -t tmpfs none /tmp
62 | mount -t devtmpfs none /dev
63 |
64 |
65 | # 停留在控制台
66 | exec /bin/sh
67 | ```
68 |
69 | 7. 用 busybox 制作 initramfs 文件
70 |
71 | ```shell
72 | # _install目录
73 | find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz
74 | ```
75 |
76 | 执行成功后可在 busybox-1.35.0 目录下找到 initramfs.cpio.gz 文件
77 |
78 | 8. 进入 qemu 目录下,执行如下命令
79 |
80 | ```shell
81 | qemu-system-aarch64 -M virt -cpu cortex-a72 -smp 8 -m 128M -kernel (your Image path) -initrd (your initramfs.cpio.gz path) -nographic -append "init=/init console=ttyAMA0"
82 | ```
83 |
84 | 其中`(your Image path)`为上一个任务中最后的`Image`镜像文件所在的目录,`(your initramfs.cpio.gz)`为步骤 7 中执行成功后得到的 initramfs.cpio.gz 的目录,例如
85 |
86 | ```shell
87 | qemu-system-aarch64 -M virt -cpu cortex-a72 -smp 8 -m 128M -kernel /home/jun/maodou/linux/arch/arm64/boot/Image -initrd /home/jun/maodou/busybox-1.35.0/initramfs.cpio.gz -nographic -append "init=/init console=ttyAMA0"
88 | ```
--------------------------------------------------------------------------------
/net.md:
--------------------------------------------------------------------------------
1 | # linux
2 | netdev_sent_queue() 用于更新已发送的数据包的数据结构的函数,它有三个参数,分别是网络设备指针,已发送的数据包个数和已发送的字节数。它的作用是将网络设备的sent_queue数据结构中的已发送的数据包个数和字节数增加相应的值,以便内核可以在合适的时机释放已发送的数据包占用的内存,例如在收到网卡的传输完成中断时。
3 |
4 | netdev_completed_queue() 用于更新已完成的I/O请求的数据结构的函数,它有三个参数,分别是网络设备指针,已完成的请求个数和已完成的字节数。它的作用是将网络设备的completed_queue数据结构中的已完成的请求个数和字节数增加相应的值,以便内核可以在合适的时机处理已完成的请求,例如释放资源、通知用户空间等。
5 |
6 | skb.eth_type_trans() skb.protocol_set() skb的protocol字段在数据包从链路层向上传递时,会被用于判断数据包的网络层协议,并调用相应的处理函数。在网卡驱动中,收到数据包后,会调用eth_type_trans函数,该函数会根据以太网帧头中的协议类型,调用skb protocol_set函数设置skb的protocol字段,然后将skb传递给netif_receive_skb函数,该函数会根据skb的protocol字段,调用相应的网络层处理函数,如ip_rcv、ipv6_rcv等。
7 |
8 | # NAPI
9 | NAPI是一种提高网络处理效率的技术,它的核心思想是在高流量负载下,不使用中断的方式读取数据,而是使用轮询的方式来检查设备是否有数据包到达。
10 |
11 | napi_complete_done() 用于通知内核NAPI设备轮询完成的函数,表示轮询处理的数据包个数。它的作用是将NAPI结构体从轮询列表中删除,并清除其状态的NAPI_STATE_SCHED位,表示不需要再被调度。它还会根据返回值判断是否需要重新开启设备的中断,以便接收新的数据包。
12 |
13 | napi_schedule() 用于调度一个NAPI设备的轮询,驱动程序应该在收到中断时调用这个函数。一个成功的调用会获取NAPI设备的所有权,然后在合适的时机调用它的poll函数来处理数据包。
14 |
15 | add_weight() weight表示每次调用NAPI的poll函数时,最多可以处理的数据包个数。通常这个值是64.
16 |
17 | NAPI的工作流程大致如下:
18 |
19 | 1.当设备收到数据包时,产生一个硬件中断,通知内核。
20 |
21 | 2.内核调用中断处理函数,关闭设备的中断,将NAPI结构体加入到轮询列表中,触发软中断。
22 |
23 | 3.软中断调用net_rx_action函数,从轮询列表中取出NAPI结构体,调用其poll函数,处理数据包。
24 |
25 | 4.poll函数每处理一个数据包,就调用napi_consume(64)来更新napi_gro_count字段,如果napi_gro_count为0,或者没有更多的GRO数据包可处理,就退出轮询模式,重新开启设备的中断,等待下一次硬件中断。poll函数返回驱动程序本次处理的数据包个数。用来判断是否达到了NAPI_POLL_WEIGHT上限。
26 |
27 | (napi_gro_count是NAPI结构体中的一个字段,它表示当前NAPI设备正在处理的GRO数据包的个数。GRO是一种通用的接收聚合技术,它的作用是将多个相同流的数据包合并成一个大的数据包,从而减少内核协议栈的处理开销。napi_gro_count的值会在每次调用napi_consume函数时减去处理的数据包个数,如果结果为0,就表示NAPI已经处理完所有的GRO数据包,可以退出轮询模式。)
28 |
29 | # FOO
30 | 以太网帧的结构,它由以下几个部分组成:
31 | - 前导码(Preamble):7个字节,用于同步发送端和接收端的时钟信号。
32 | - 起始定界符(Start of Frame Delimiter,SFD):1个字节,用于标识帧的开始。
33 | - 目的地址(Destination Address,DA):6个字节,用于指定接收端的MAC地址。
34 | - 源地址(Source Address,SA):6个字节,用于指定发送端的MAC地址。
35 | - 类型/长度(Type/Length):2个字节,用于指定帧的类型或长度。
36 | - 数据(Data):46~1500个字节,用于存储帧的有效载荷,包括其他协议的报头和数据。
37 | - 帧检验序列(Frame Check Sequence,FCS):4个字节,用于存储CRC校验码,用于检测帧在传输过程中是否出现错误。
38 |
39 | fujita rust-e1000中length -= 4的操作是为了去掉FCS。
--------------------------------------------------------------------------------
/plan.md:
--------------------------------------------------------------------------------
1 | # OS训练营第三阶段选修
2 | 时间:2023-11-04~2023-12-03
3 | 课题:Rust for Linux 驱动模块开发
4 | 导师: XLY, CL
5 |
6 | ## 课题目标
7 | 学生选修该课题,可以学习到如何一步一步地开发一个Rust for Linux内核驱动;逐步积累并形成学习开发文档;进而有机会为rcore-os Rust操作系统社区以及Linux社区的发展,通过编写Rust驱动等方式贡献一份自己的力量。
8 |
9 | ## 考核方式
10 | * 通过课程练习和实习项目,进行成绩打分排名;
11 | * 阶段性练习及每周的引导 60%; 基于物理开发板/模拟器的Rust驱动开发项目 40%
12 | * 将会统计各位学员的姓名及仓库地址; 代码及运行结果截图请提交在各自练习的仓库; 结果截图中需出现自己的名字或Github ID
13 |
14 |
15 | ## 课题及实习的安排表
16 |
17 | | 周 | 日期 | 导师 | 课程 | 练习 | 练习分数 |
18 | |:--:|:--:|:--:|:--|:--|:--:|
19 | |第一周 基础| 2023-11-6 | XLY | Rust for Linux的基本框架介绍 | 练习1: 获取Rust for Linux源码,配置环境并编译通过,并在模拟器中运行。| 15% |
20 | |第一周 基础| 2023-11-9 | CL | Rust内核驱动模块的各个基础组成部分的学习,结合已有rust-for-linux案例驱动的学习 | 练习2: 编写一个简单的"Hello World"的Rust内核模块,并能加载运行输出。| 15% |
21 | |第二周 进阶| 2023-11-13 | CL | 内核驱动的开发框架介绍,以网卡驱动为例 | 练习3: 基于Qemu模拟器上的e1000网卡驱动框架,填充驱动初始化函数,并可以自定义一个linux内核C函数在Rust网卡驱动模块中调用该函数。| 15% |
22 | |第二周 进阶| 2023-11-16 | XLY | Rust网卡驱动的开发 | 练习4: 基于Qemu模拟器上的e1000网卡驱动框架,填充驱动的接收包和发送包函数,并可以加载运行,运行ping的发送与接收响应。| 15% |
23 | |第二周 进阶| | | | 练习3, 4(可选): 分析并复现rust-for-linux社区已实现的驱动案例,可复现运行并做个报告。| 30% |
24 | | | | | | | |
25 | | 第三周 实习准备 | 2023-11-20 | XLY | 实习项目安排介绍; Uart及VirtIO驱动项目讲解 | 对于实习项目,准备Rust for Linux在所选平台上的基本运行所需的工作,如引导启动所需的设备树,内存设备基地址等 | |
26 | | 第三周 实习准备 | 2023-11-23 | CL | 项目问题交流; 精简C语言版本驱动介绍 | 对于所选的驱动,如Uart或VirtIO驱动, 为下一步把最小化C版本驱动转换为Rust版本驱动做准备;精简其C代码, 使其保留最基本的运行功能; | |
27 | | 第四周 项目实习 | 2023-11-27 | XLY | 课程内容回顾,实习项目交流 | 实习项目1 (可选): 在华山派开发板或树莓派模拟器上,适配Rust for Linux 6.6内核,并实现Rust Uart串口驱动,运行起来;| 40% |
28 | | 第四周 项目实习 | 2023-11-30 | CL | 课程内容回顾,实习项目交流 | 实习项目2 (可选): 在Qemu模拟器上尝试把已有的rcore-os/virtio-drivers项目,移植适配于Rust for Linux 6.6内核,驱动运行起来;| 40% |
29 |
--------------------------------------------------------------------------------
/优秀文档参考/PuzzleFS-下一代Linux容器文件系统之源码分析报告-邝劲强.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: PuzzleFS-下一代Linux容器文件系统之源码分析报告-邝劲强
3 | Date: 2023-11
4 | Tags:
5 | - Author: 邝劲强
6 | - Reports Repo: https://github.com/lispking/rust-for-linux/tree/main/reports
7 | ---
8 |
9 | # PuzzleFS - 下一代 Linux 容器文件系统之源码分析报告
10 |
11 | PuzzleFS 项目地址:https://github.com/project-machine/puzzlefs
12 |
13 | > PuzzleFS 号称是`下一代 Linux 容器文件系统`,采用 Rust 语言编写,具有`快速镜像构建`、`直接挂载支持`、`内存安全保证`等功能。
14 |
15 | ---
16 |
17 | ## PuzzleFS 项目介绍
18 |
19 | 思科开发者 `Ariel Miculas` 在[内核邮件列表](https://lore.kernel.org/rust-for-linux/20230609063118.24852-1-amiculas@cisco.com/)中,发布了用 Rust 写的 PuzzleFS 文件系统驱动,以征求其他开发者的意见,目前这一驱动程序处于“概念验证”阶段。
20 |
21 | PuzzleFS 是一种`只读`的文件系统,与 `Open Container Initiative (OCI) v2 image specification` 的设计目标相符,它使用内容定义进行分块,并且使用内容寻址的数据存储,旨在解决现有 OCI 格式的限制。
22 |
23 | > OCI 镜像规范的第一个版本存在许多问题,这些问题的根源都是来自依赖 tar 存档来保存文件系统中的层,事实上 `tar 不适合解决容器文件系统问题`。
24 |
25 | PuzzleFS 旨在解决 tar 的问题。文件系统镜像本身由一组放置在底层文件系统上的文件组成。与 `OCI 镜像格式一样`,存在一个顶级 `index.json` 文件,其中包含一组标签,每个标签代表某一个版本的文件系统,并指向一个清单文件(`manifest file`)。清单文件又指向镜像配置和存储在实际镜像层中的数据。其他所有内容都存储为 `blobs/sha256` 目录中的一组 `blob`。
26 |
27 | 文件系统中的大多数数据都被分成可变大小的块(`chunk`),以内容的 `SHA256` 哈希作为文件名来存储为 `blob`。这个分块动作本身是使用 `FastCDC 算法`进行的,该算法查找 "切割点(`cut points`)",把数据流分为不同大小的 `blob`。任何一个特定的流(例如文件的内容)都可以分成`五个`或`五十个块`,这取决于如何确定这些切割点;然后,每个块都以不同的 `blob` 方式落在 `blobs/sha256` 下,并将其哈希添加到清单中。
28 |
29 | ---
30 |
31 | ## 环境准备
32 |
33 | ### 实验环境信息
34 |
35 | * CPU: Apple M1
36 | * 宿主机OS:macOS Sonoma 14.1.1
37 | * 虚拟机:使用 OrbStack 安装 ubuntu 23.10,选用 OrbStack 理由看 这里:https://docs.orbstack.dev/
38 |
39 | ### Ubuntu 环境准备
40 |
41 | * 设置 Rustup 镜像, 修改配置 ~/.zshrc or ~/.bashrc
42 |
43 | ```bash
44 | export RUSTUP_DIST_SERVER="https://rsproxy.cn"
45 | export RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup"
46 | ```
47 |
48 | * 安装 Rust(请先完成步骤一的环境变量导入并 source rc 文件或重启终端生效)
49 |
50 | ```bash
51 | curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh
52 | ```
53 |
54 | * 设置 crates.io 镜像, 修改配置 ~/.cargo/config,已支持git协议和sparse协议,>=1.68 版本建议使用 sparse-index,速度更快。
55 |
56 | ```bash
57 | cat < ~/.cargo/config
58 | [source.crates-io]
59 | replace-with = 'rsproxy-sparse'
60 | [source.rsproxy]
61 | registry = "https://rsproxy.cn/crates.io-index"
62 | [source.rsproxy-sparse]
63 | registry = "sparse+https://rsproxy.cn/index/"
64 | [registries.rsproxy]
65 | index = "https://rsproxy.cn/crates.io-index"
66 | [net]
67 | git-fetch-with-cli = true
68 | EOF
69 | ```
70 |
71 | * 修改 Ubuntu 源,加速依赖包更新速度
72 |
73 | ```bash
74 | sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
75 | sudo sed -i 's/ports.ubuntu.com/mirrors.ustc.edu.cn/' /etc/apt/sources.list
76 | ```
77 |
78 | * 安装 Linux 内核相关依赖包
79 |
80 | ```bash
81 | ## first Update
82 | sudo apt update
83 |
84 | ## Install dependency packages
85 | sudo apt -y install \
86 | binutils build-essential libtool texinfo \
87 | gzip zip unzip patchutils curl git \
88 | make cmake ninja-build automake bison flex gperf \
89 | grep sed gawk bc \
90 | zlib1g-dev libexpat1-dev libmpc-dev \
91 | libglib2.0-dev libfdt-dev libpixman-1-dev libelf-dev libssl-dev
92 |
93 | ## Install the LLVM
94 | sudo apt -y install clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang
95 | ```
96 |
97 | * 安装 PuzzleFS 需要用到的工具
98 |
99 | ```bash
100 | sudo apt install -y capnproto skopeo umoci tree jq
101 | ```
102 |
103 | ---
104 |
105 | ## 编译和挂载 PuzzleFS 镜像
106 |
107 | ### 测试文件准备
108 |
109 | ```bash
110 | # 1. 创建相关测试目录
111 | mkdir -p /tmp/example-rootfs/algorithms /tmp/mounted-image
112 |
113 | # 2. 创建测试文件
114 | echo "Binary Search" > /tmp/example-rootfs/algorithms/binary-search.txt
115 | echo "test" > /tmp/example-rootfs/algorithms/test.txt
116 | echo "lorem ipsum" > /tmp/example-rootfs/lorem_ipsum.txt
117 | tree /tmp/example-rootfs
118 | ```
119 |
120 | ### 编译 PuzzleFS 源码
121 |
122 | ```bash
123 | cargo build --release
124 | ```
125 |
126 | ### 构建 PuzzleFS 镜像
127 |
128 | ```bash
129 | target/release/puzzlefs build -c /tmp/example-rootfs /tmp/puzzlefs-image puzzlefs_example
130 | ```
131 |
132 | 运行成功会产生以下信息,`build` 命令内部实际通过 Linux 内核工具模块 `fs-verity` 实现。
133 |
134 | ```bash
135 | puzzlefs image manifest digest: 743b08e98105e637c47adb80a17ba21ed12eb31523a056a0970ff687551904d7
136 | ```
137 |
138 | ### 挂载 PuzzleFS 镜像
139 |
140 | * 通过 PuzzleFS 工具挂载到系统
141 |
142 | ```bash
143 | target/release/puzzlefs mount /tmp/puzzlefs-image puzzlefs_example /tmp/mounted-image
144 | ```
145 |
146 | * 验证挂载结果
147 |
148 | ```bash
149 | # 1. 验证挂载结果
150 | mount|grep fuse
151 |
152 | # 若显示结果出现 /dev/fuse on /tmp/mounted-image,则代表镜像挂载成功
153 | /dev/fuse on /tmp/mounted-image type fuse (rw,nosuid,nodev,relatime,user_id=501,group_id=501)
154 |
155 | # 2. 验证挂载目录情况是否与 /tmp/example-rootfs 内容一致
156 | tree /tmp/mounted-image
157 |
158 | # 若显示结果如下,则代表文件挂载成功
159 | /tmp/mounted-image
160 | ├── algorithms
161 | │ ├── binary-search.txt
162 | │ └── test.txt
163 | └── lorem_ipsum.txt
164 |
165 | # 3. 查看 PuzzleFS 文件组织结构
166 | tree /tmp/puzzlefs-image/
167 |
168 | # 显示内容如文章介绍所述
169 | /tmp/puzzlefs-image/
170 | ├── blobs
171 | │ └── sha256
172 | │ ├── 04718eb872552abfdf44d563aa09bdf89343950a364d9ff555fc6e3657d383b4
173 | │ ├── 73ee73b991e91697ec02c990de8401d7c316d3392ec394be7162195dc783c9b5
174 | │ └── e10090d2c24f6a299ad6fee3e6f345a784918bc89530a1329c92c40de71a9c47
175 | ├── index.json
176 | └── oci-layout
177 |
178 | # 4. 查看 /tmp/puzzlefs-image/index.json 内容
179 | cat /tmp/puzzlefs-image/index.json | jq .
180 |
181 | # manifests 里的 digest 确实指示到 /tmp/puzzlefs-image/blobs/sha256 下
182 | {
183 | "schemaVersion": -1,
184 | "manifests": [
185 | {
186 | "digest": "sha256:04718eb872552abfdf44d563aa09bdf89343950a364d9ff555fc6e3657d383b4",
187 | "size": 272,
188 | "media_type": "application/vnd.puzzlefs.image.rootfs.v1",
189 | "annotations": {
190 | "org.opencontainers.image.ref.name": "puzzlefs_example"
191 | }
192 | }
193 | ],
194 | "annotations": {}
195 | }
196 |
197 | # 5. 复制内容验证文件
198 | mkdir -p ~/puzzlefs/format
199 | cp -f puzzlefs-lib/src/format/*.capnp ~/puzzlefs/format
200 |
201 | # 6. 验证 PuzzleFS 文件系统 metadata 内容,注意 Rootfs 文件在 /tmp/puzzlefs-image/index.json 的 digest 字段已提供
202 | capnp convert binary:json ~/puzzlefs/format/manifest.capnp Rootfs < /tmp/puzzlefs-image/blobs/sha256/04718eb872552abfdf44d563aa09bdf89343950a364d9ff555fc6e3657d383b4
203 |
204 | # 这里可以看到文件的压缩开发、偏移以及文件验证信息等元数据均记录在此文件
205 | { "metadatas": [{ "digest": [115, 238, 115, 185, 145, 233, 22, 151, 236, 2, 201, 144, 222, 132, 1, 215, 195, 22, 211, 57, 46, 195, 148, 190, 113, 98, 25, 93, 199, 131, 201, 181],
206 | "offset": "0",
207 | "compressed": false }],
208 | "fsVerityData": [
209 | { "digest": [115, 238, 115, 185, 145, 233, 22, 151, 236, 2, 201, 144, 222, 132, 1, 215, 195, 22, 211, 57, 46, 195, 148, 190, 113, 98, 25, 93, 199, 131, 201, 181],
210 | "verity": [175, 40, 167, 26, 25, 10, 180, 65, 203, 152, 77, 163, 62, 196, 210, 184, 253, 43, 13, 128, 130, 115, 124, 42, 166, 42, 113, 75, 36, 144, 91, 6] },
211 | { "digest": [225, 0, 144, 210, 194, 79, 106, 41, 154, 214, 254, 227, 230, 243, 69, 167, 132, 145, 139, 200, 149, 48, 161, 50, 156, 146, 196, 13, 231, 26, 156, 71],
212 | "verity": [224, 250, 182, 10, 67, 174, 109, 47, 213, 23, 121, 57, 246, 130, 220, 70, 235, 42, 198, 50, 201, 130, 116, 91, 120, 12, 232, 61, 237, 227, 113, 94] } ],
213 | "manifestVersion": "2" }
214 |
215 | # 7. 验证 PuzzleFS 文件系统 inode 内容
216 | capnp convert binary:json ~/puzzlefs/format/metadata.capnp InodeVector < /tmp/puzzlefs-image/blobs/sha256/73ee73b991e91697ec02c990de8401d7c316d3392ec394be7162195dc783c9b5
217 |
218 | # 可以看到日常看到的 ls -lat 命令查看的文件 dir、file 及对应的归属用户和权限都保存在这个文件里
219 | {"inodes": [
220 | { "ino": "1",
221 | "mode": {"dir": {
222 | "entries": [
223 | { "ino": "2",
224 | "name": [97, 108, 103, 111, 114, 105, 116, 104, 109, 115] },
225 | { "ino": "3",
226 | "name": [108, 111, 114, 101, 109, 95, 105, 112, 115, 117, 109, 46, 116, 120, 116] } ],
227 | "lookBelow": false }},
228 | "uid": 501,
229 | "gid": 501,
230 | "permissions": 493 },
231 | { "ino": "2",
232 | "mode": {"dir": {
233 | "entries": [
234 | { "ino": "4",
235 | "name": [98, 105, 110, 97, 114, 121, 45, 115, 101, 97, 114, 99, 104, 46, 116, 120, 116] },
236 | {"ino": "5", "name": [116, 101, 115, 116, 46, 116, 120, 116]} ],
237 | "lookBelow": false }},
238 | "uid": 501,
239 | "gid": 501,
240 | "permissions": 493 },
241 | { "ino": "3",
242 | "mode": {"file": [{ "blob": {
243 | "digest": [225, 0, 144, 210, 194, 79, 106, 41, 154, 214, 254, 227, 230, 243, 69, 167, 132, 145, 139, 200, 149, 48, 161, 50, 156, 146, 196, 13, 231, 26, 156, 71],
244 | "offset": "0",
245 | "compressed": false },
246 | "len": "12" }]},
247 | "uid": 501,
248 | "gid": 501,
249 | "permissions": 420 },
250 | { "ino": "4",
251 | "mode": {"file": [{ "blob": {
252 | "digest": [225, 0, 144, 210, 194, 79, 106, 41, 154, 214, 254, 227, 230, 243, 69, 167, 132, 145, 139, 200, 149, 48, 161, 50, 156, 146, 196, 13, 231, 26, 156, 71],
253 | "offset": "12",
254 | "compressed": false },
255 | "len": "14" }]},
256 | "uid": 501,
257 | "gid": 501,
258 | "permissions": 420 },
259 | { "ino": "5",
260 | "mode": {"file": [{ "blob": {
261 | "digest": [225, 0, 144, 210, 194, 79, 106, 41, 154, 214, 254, 227, 230, 243, 69, 167, 132, 145, 139, 200, 149, 48, 161, 50, 156, 146, 196, 13, 231, 26, 156, 71],
262 | "offset": "26",
263 | "compressed": false },
264 | "len": "5" }]},
265 | "uid": 501,
266 | "gid": 501,
267 | "permissions": 420 } ]}
268 | ```
269 |
270 | > 至此,PuzzleFS 镜像则准备完成,接下来,就可以开始从源码级别解刨 PuzzleFS 实现。
271 |
272 | ---
273 |
274 | ## 解刨前,需了解的知识
275 |
276 | ### [Image Format Specification](https://github.com/opencontainers/image-spec/blob/main/spec.md)
277 |
278 | OCI manifest主要由一个或多个文件列表(组成一个完整的可运行的文件系统)、索引和配置等元数据组成。其中,配置文件包含应用参数、环境变量等信息。索引文件主要是文件清单列表描述,不同平台可能会所有差异。
279 |
280 | 
281 |
282 | OCI 镜像构建完成后,可通过名称方式发现和下载镜像,以及通过哈希方式进行验证,通过签名进行信任,并解压到 OCI 运行时中。
283 |
284 | 
285 |
286 | OCI 规范主要包括下面这些组件:
287 | * Image Manifest - 有关容器镜像文件清单说明
288 | * Image Index - 有关 Manifest 索引列表说明
289 | * Image Layout - 有关镜像中的文件系统布局说明
290 | * Filesystem Layer - 有关容器文件系统的变化说明
291 | * Image Configuration - 有关镜像的配置说明
292 | * Conversion - 有关转换如何发生的说明
293 | * Artifacts Guidance - 除 OCI 镜像之外的打包规范说明
294 | * Descriptor - 有关类型、元数据和内容地址等的说明
295 |
296 | ---
297 |
298 | ## PuzzleFS 源码分析
299 |
300 | 该工程主要包含`puzzlefs-lib`库和`exe`可执行两部分:
301 |
302 | * `puzzlefs-lib`:PuzzleFS 库的实现
303 | - `format`:用于 PuzzleFS 格式的序列化和反序列化
304 | - `builder`:用于构建 PuzzleFS 镜像
305 | - `extractor`:用于解压 PuzzleFS 镜像
306 | - `reader`: 用于将 PuzzleFS 镜像挂载到 `FUSE`(文件系统)的模块。
307 | * `exe/`:可执行文件封装主要是对一些命令和库进行组合,将其整合在一起。这个封装没有什么特别需要解读的地方,因为它只是简单地将相关功能集成在一起,方便用户使用。
308 |
309 | ---
310 |
311 | ### PuzzleFS 格式
312 |
313 | PuzzleFS 主要由两部分组成:一个用于存储 inode 信息的元数据格式,以及由各种分块算法定义的实际文件系统数据块。其中,所有枚举值均编码为 u32;所有编码均为小端。
314 |
315 | 所有 PuzzleFS 块均由以下结构体封装:
316 |
317 | ```rust
318 | enum hash {
319 | sha256,
320 | }
321 |
322 | typedef hash_value byte[32] // 对于 sha256
323 |
324 | enum blob_type {
325 | root,
326 | metadata,
327 | file,
328 | }
329 |
330 | type puzzlefs_blob {
331 | enum hash;
332 | u64 references_len;
333 | hash_value references[];
334 | blob_type type;
335 | // followed by the actual blob
336 | }
337 | ```
338 |
339 | > 下面通过一张 UML 图来解读 Puzzle 文件系统格式里相关组件的关系,源码文件位于 `format/types.rs`
340 |
341 | 
342 |
343 |
344 | #### 根文件系统(Rootfs)的结构体定义
345 |
346 | ```rust
347 | pub struct Rootfs {
348 | pub metadatas: Vec,
349 | pub fs_verity_data: VerityData,
350 | pub manifest_version: u64,
351 | }
352 | ```
353 |
354 | > 用于描述一个 Rootfs 实例,其中包含了文件系统的元数据、完整性数据和清单版本号。这有助于系统和应用程序正确地处理和验证根文件系统的内容。
355 |
356 | 结构体包含以下成员:
357 |
358 | 1. `metadatas`:一个`Vec`类型的成员,表示根文件系统的元数据。`BlobRef`可能是一个指向文件系统元数据的引用或指针。
359 | 2. `fs_verity_data`:一个`VerityData`类型的成员,表示文件系统的完整性数据。`VerityData`可能是一个用于验证文件系统数据完整性的结构体或类。
360 | 3. `manifest_version`:一个`u64`类型的成员,表示清单(manifest)的版本。这个版本号用于识别和区分不同的清单版本。
361 |
362 |
363 | #### Blob 引用(BlobRef)的结构体定义
364 |
365 | ```rust
366 | pub struct BlobRef {
367 | pub digest: [u8; SHA256_BLOCK_SIZE],
368 | pub offset: u64,
369 | pub compressed: bool,
370 | }
371 | ```
372 |
373 | > 用于文件系统中引用和标识 BLOB 数据。通过存储 BLOB 的哈希值、偏移量和压缩状态,这个结构体为文件系统的管理和操作提供了有关 BLOB 信息的基本概述。
374 |
375 | 结构体包含以下成员:
376 |
377 | 1. `digest`:`[u8; SHA256_BLOCK_SIZE]`类型,用于存储 BLOB 的哈希值。SHA256 是一种加密哈希算法,用于生成一个固定长度的哈希值。这个字节数组的长度为 SHA256 块大小,通常为 64 字节。
378 | 2. `offset`:`u64` 类型,表示 BLOB 在文件系统中的偏移量。这个偏移量用于定位 BLOB 在文件系统中的位置。
379 | 3. `compressed`:`bool` 类型,表示 BLOB 是否已压缩。如果为 `true`,则表示 BLOB 已压缩;如果为 `false`,则表示 BLOB 未压缩。
380 |
381 | #### inode(节点)的结构体定义
382 |
383 | ```rust
384 | pub struct Inode {
385 | pub ino: Ino,
386 | pub mode: InodeMode,
387 | pub uid: u32,
388 | pub gid: u32,
389 | pub permissions: u16,
390 | pub additional: Option,
391 | }
392 | ```
393 |
394 | > 用于表示文件系统中的 inode,包含了识别和描述 inode 所需的基本信息。这有助于管理和操作文件系统中的文件和目录。
395 |
396 | 结构体包含以下成员:
397 | 1. `ino`:`Ino` 类型,表示 inode 的唯一标识符。inode 是文件系统中的一个基本概念,用于标识文件和目录。
398 | 2. `mode`:`InodeMode` 类型,表示 inode 的模式。`InodeMode` 可能是一个枚举类型,用于表示 inode 是文件还是目录,以及文件或目录的权限设置。
399 | 3. `uid`:`u32` 类型,表示 inode 所属的用户 ID。用户 ID 用于识别文件或目录的所有者。
400 | 4. `gid`:`u32` 类型,表示 inode 所属的组 ID。组 ID 用于识别文件或目录的所有者所属的组。
401 | 5. `permissions`:`u16` 类型,表示 inode 的权限。这些权限通常包括读、写和执行权限,用于控制用户和组对文件或目录的访问。
402 | 6. `additional`:`Option` 类型,表示额外的 inode 信息。`InodeAdditional` 可能是一个包含更多详细信息的结构体或类。这个选项值为 `None` 时,表示没有额外的 inode 信息。
403 |
404 |
405 | #### 元数据 BLOB(MetadataBlob)的结构体定义
406 |
407 | ```rust
408 | pub struct MetadataBlob {
409 | reader: message::TypedReader<
410 | ::capnp::serialize::BufferSegments,
411 | crate::metadata_capnp::inode_vector::Owned,
412 | >,
413 | }
414 | ```
415 |
416 | > 用于表示一个元数据 BLOB,包含了读取和解析元数据所需的信息。这个结构体通常用于处理和分析文件系统中的元数据,以便更好地管理和操作文件。
417 |
418 | 结构体只有一个成员 `reader`:
419 |
420 | 这是一个 `message::TypedReader` 类型,用于读取和解析元数据 BLOB。`TypedReader` 是 `Cap'n Proto` 序列化库中的一个类型,用于读取和处理特定类型的数据。
421 |
422 | `::capnp::serialize::BufferSegments` 是一个泛型类型,表示用于存储元数据的内存映射(`Mmap`)缓冲区。`Mmap` 是一种内存映射文件的技术,可以将文件内容映射到内存中,以便更快地访问和处理文件数据。
423 |
424 | `crate::metadata_capnp::inode_vector::Owned` 是一个泛型类型,表示元数据 BLOB 中的 inode 向量。inode 向量可能是一个包含 inode 结构体的列表或数组,用于存储文件系统中的元数据。
425 |
426 | ---
427 |
428 | ### PuzzleFS 镜像构建
429 |
430 | > 用于处理文件系统、压缩、加密或构建的工具或库。它提供了一些方法来操作文件系统、处理压缩、进行测试,并且与文件系统的可复现性或安全性有关。
431 |
432 | #### 核心结构体定义
433 |
434 | > 文件位于 `puzzlefs-lib/src/builder/filesystem.rs`
435 |
436 | ```rust
437 | struct ReaderLink {
438 | file: PathBuf,
439 | done: bool,
440 | }
441 |
442 | /// A structure used to chain multiple readers, similar to
443 | /// [chain](https://doc.rust-lang.org/std/io/trait.Read.html#method.chain)
444 | /// and [multi_reader](https://docs.rs/multi_reader/latest/multi_reader/)
445 | pub struct FilesystemStream {
446 | reader_chain: Vec,
447 | current_reader: Option,
448 | }
449 | ```
450 |
451 | 该部分主要由两个结构体:`ReaderLink` 和 `FilesystemStream` 组成。
452 |
453 | 1. `ReaderLink` 结构体包含两个字段:
454 | - `file`:一个 `PathBuf` 类型的字段,表示文件的路径。
455 | - `done`:一个 `bool` 类型的字段,表示读取操作是否完成。
456 |
457 | 2. `FilesystemStream` 结构体用于链接多个 `Reader`,类似于 `std::io::Read` trait 的 `chain` 方法和 `multi_reader` 库,该做法的目的是方便按顺序读取多个文件。它包含两个字段:
458 | - `reader_chain`:一个 `Vec` 类型的字段,用于存储多个 `ReaderLink`,实现类似于链式文件读取的操作,例如,在读取一个文件之后自动切换到下一个文件。这种设计可以方便地实现流水线式的文件处理任务。
459 | - `current_reader`:一个 `Option` 类型的字段,表示当前正在使用的`reader`。
460 |
461 | 3. 两个结构体关系如下图所述:
462 |
463 | 
464 |
465 |
466 | #### 核心函数作用解读
467 |
468 | > 文件位于 `puzzlefs-lib/src/builder.rs`
469 |
470 | * `walker`: 用于遍历文件系统或目录结构。
471 | * `add_entry`, `serialize_manifest`, `serialize_metadata`: 与将文件系统或目录结构的信息添加到某个数据结构或序列化有关。
472 | * `process_chunks`, `build_delta`: 与处理文件或数据块的压缩相关,其中, `C` 代表某种压缩算法。
473 | * `lookup_existing`: 用于查找或查询文件系统中的现有文件或目录。
474 | * `build_initial_rootfs`, `add_rootfs_delta`: 与构建或修改文件系统根目录有关。
475 | * `enable_fs_verity`: 用于启用文件系统 verity 相关的功能。
476 | * `build_test_fs`, `test_fs_generation`, `test_delta_generation`: 与测试文件系统或其生成有关。
477 | * `do_vecs_match`: 用于比较两个序列或向量是否相等的。
478 | * `get_image_blobs`: 用于获取镜像或数据块的。
479 | * `same_dir_reproducible`, `same_dir_contents_reproducible`: 与检查目录或文件内容的可复现性有关。
480 | * `test_reproducibility`: 用于测试或验证文件系统的可复现性。
481 | * `build_dummy_fs`: 用于构建虚拟的文件系统。
482 |
483 | ---
484 |
485 | ### PuzzleFS 镜像解压
486 |
487 | > 用于从容器或镜像中提取文件系统的工具或库。它提供了一些方法来处理文件和目录的创建、所有权和权限。
488 |
489 | > 文件位于 `puzzlefs-lib/src/extractor.rs`
490 |
491 | * `runs_privileged`: 用于检查当前运行是否具有 root 权限。
492 | * `PuzzleFS`, `WalkPuzzleFS``: 用于处理文件系统的类或结构体。
493 | * `makedev`, `mknod`, `Mode`, `SFlag`: 与文件系统或设备创建有关的函数或常量。
494 | * `chown`, `mkfifo`, `symlinkat`, `Gid`, `Uid`: 与文件系统所有权、符号链接或 FIFO 管道创建有关的函数或常量。
495 | * `HashMap`, `OsStr`, `Path`, `PathBuf`: 与文件系统路径或数据结构有关的类型。
496 | * `fs`, `io`: 与文件系统操作或输入/输出操作有关的模块。
497 |
498 | ---
499 |
500 | ### PuzzleFS 镜像挂载
501 |
502 | > 用于实现一个基于 FUSE 的文件系统,并可能与 puzzles 或者 cryptographic hash 有关。
503 |
504 | #### `PuzzleFS`结构体定义
505 |
506 | > 文件位于 `puzzlefs-lib/src/reader/puzzlefs.rs`
507 |
508 | > 用于在Rust中实现镜像挂载功能,并与其他组件(如`Fuse`)协同工作,表示一个包含镜像、层(layer)元数据、验证数据和可选验证映像清单的文件系统。
509 |
510 | ```rust
511 | pub struct PuzzleFS {
512 | pub oci: Arc,
513 | layers: Vec,
514 | pub verity_data: Option,
515 | pub manifest_verity: Option>,
516 | }
517 | ```
518 | 以下是结构体中各属性的解释:
519 | 1. `oci`:`Arc`类型,`Arc`用于确保镜像在多个引用之间共享,而不会创建不必要的副本。
520 | 2. `layers`:`Vec`类型,表示镜像的各个层。`MetadataBlob`定义参考 `Format 章节`的描述。
521 | 3. `verity_data`:`Option`类型,表示验证数据。验证数据用于确保镜像在传输过程中不被篡改。可选字段,意味着并非所有`PuzzleFS`实例都需要验证数据。
522 | 4. `manifest_verity`:`Option>`类型的,表示镜像清单的验证。可选字段,用于确保镜像的完整性。清单验证通常是通过将清单内容与预期的哈希值进行比较来实现的。
523 |
524 |
525 | #### `Fuse`结构体定义
526 |
527 | > 文件位于 `puzzlefs-lib/src/reader/fuse.rs`
528 |
529 | ```rust
530 | pub enum PipeDescriptor {
531 | UnnamedPipe(PipeWriter),
532 | NamedPipe(PathBuf),
533 | }
534 |
535 | pub struct Fuse {
536 | pfs: PuzzleFS,
537 | sender: Option>,
538 | init_notify: Option,
539 | // TODO: LRU cache inodes or something. I had problems fiddling with the borrow checker for the
540 | // cache, so for now we just do each lookup every time.
541 | }
542 | ```
543 |
544 | > 用于实现基于 FUSE(Filesystem in Userspace)的文件系统,允许用户空间实现文件系统功能。`PipeDescriptor` 用于在进程间通信,`Fuse` 结构体则用于处理 FUSE 文件系统的相关操作。
545 |
546 | * `PipeDescriptor` 枚举类型有两个成员:
547 | - `UnnamedPipe`:`PipeWriter` 实例,表示未命名管道。
548 | - `NamedPipe`:`PathBuf` 实例,表示已命名管道。
549 |
550 | * `Fuse` 结构体有以下成员:
551 | - `pfs`:`PuzzleFS` 实例,表示 Puzzle 文件系统。
552 | - `sender`:`Option>` 类型,用于发送消息。
553 | - `init_notify`:一个 `Option` 类型,表示一个初始化通知管道。
554 | - 注释中提到了一个 LRU 缓存,但由于借用检查器的问题,暂时每次都进行查找。
555 |
556 |
557 | #### `WalkPuzzleFS`结构体定义
558 |
559 | > 文件位于 `puzzlefs-lib/src/reader/walk.rs`
560 |
561 | ```rust
562 | /// A in iterator over a PuzzleFS filesystem. This iterates breadth first, since file content is
563 | /// stored that way in a puzzlefs image so it'll be faster reading actual content if clients want
564 | /// to do that.
565 | pub struct WalkPuzzleFS<'a> {
566 | pfs: &'a mut PuzzleFS,
567 | q: VecDeque,
568 | }
569 |
570 | pub struct DirEntry {
571 | oci: Arc,
572 | pub path: PathBuf,
573 | pub inode: Inode,
574 | }
575 | ```
576 |
577 | > 用于遍历 Puzzle 文件系统,以便于查询和操作文件系统中的内容。迭代器 `WalkPuzzleFS` 负责遍历文件系统,而 `DirEntry` 表示每个目录 entry,便于进一步处理。
578 |
579 | * `WalkPuzzleFS` 表示一个遍历 Puzzle 文件系统的迭代器。它采用广度优先遍历策略,因为文件内容在 Puzzle 文件系统镜像中就是以这种方式存储的,这样在实际内容读取时会更快。结构体有以下成员:
580 | - `pfs`:`PuzzleFS` 实例,表示一个 Puzzle 文件系统。
581 | - `q`:`VecDeque` 类型,表示一个待遍历的目录 entry 队列。
582 |
583 | * `DirEntry` 表示一个目录 entry,包含镜像、路径和 inode 编号等信息。结构体有以下成员:
584 | - `oci`:`Arc` 类型,表示一个镜像。
585 | - `path`:`PathBuf` 类型,表示目录 entry 的路径。
586 | - `inode`:`Inode` 类型,表示目录 entry 的 inode 编号。
587 |
588 | #### 核心函数作用解读
589 |
590 | > 文件位于 `puzzlefs-lib/src/reader.rs`
591 |
592 | * `mount_option_from_str` 函数:用于从字符串`s`中解析出对应的 `FUSE` 挂载选项(`fuse_ffi::MountOption`)。函数采用匹配语法 `match` 来根据字符串`s`的值进行匹配,然后返回相应的挂载选项。以下是代码中各挂载选项的描述:
593 | - `auto_unmount`:自动卸载文件系统。
594 | - `allow_other`:允许其他用户访问文件系统。
595 | - `allow_root`:允许root用户访问文件系统。
596 | - `default_permissions`:使用默认权限。
597 | - `dev`:启用设备文件。
598 | - `nodev`:禁用设备文件。
599 | - `suid`:启用 `SUID`(`Set UID`)特权。
600 | - `nosuid`:禁用 `SUID` 特权。
601 | - `ro`:只读挂载。
602 | - `rw`:读写挂载。
603 | - `exec`:启用可执行文件。
604 | - `noexec`:禁用可执行文件。
605 | - `atime`:使用文件访问时间(`ATIME`)更新。
606 | - `noatime`:不使用文件访问时间更新。
607 | - `dirsync`:启用目录同步。
608 | - `sync`:启用同步挂载。
609 | - `async`:启用异步挂载。
610 | - `fsname`:设置文件系统名称。
611 | - `subtype`:设置文件系统子类型。
612 | - 如果`s`的字符串值不匹配以上任何一种挂载选项,则返回`fuse_ffi::MountOption::CUSTOM`(表示自定义挂载选项),并将字符串`s`作为参数传递。
613 |
614 | * `mount`函数:用于将一个镜像(Image)挂载到指定的目录(mountpoint)。以下是代码的主要部分及其功能:
615 | * 参数解读:
616 | - `image`:要挂载的镜像。
617 | - `tag`:镜像的标签。
618 | - `mountpoint`:要挂载镜像到的目录路径。
619 | - `options`:字符串切片,表示挂载选项。
620 | - `init_notify`:可选的管道描述符,用于初始化通知。
621 | - `manifest_verity`:可选的字节切片,表示镜像清单的验证。
622 | * 函数流程:
623 | - 首先,尝试打开一个名为`PuzzleFS`的文件系统,该文件系统包含镜像、标签和可选的验证。
624 | - 然后,创建一个`Fuse`实例,将`PuzzleFS`挂载到`mountpoint`。
625 | - 使用`fuse_ffi::mount2`函数将`Fuse`实例挂载到指定路径,并传递挂载选项。(另一个 `spawn_mount` 函数就不展开说明,差异点是此处调用`fuse_ffi::spawn_mount2`实现)
626 | - 最后,返回成功挂载的`Ok(())`结果。
627 |
628 | ---
629 |
630 | ## [Chunking 文章解读](chunking.md)
631 |
632 | 这篇文章主要讨论了如何将文件系统进行分块的技术。作者提出了两种分块策略:基于`Rabin指纹/滚动哈希`的方法和基于`内容定义`的分块方法。
633 |
634 | 首先,作者建议使用`Rabin指纹/滚动哈希技术`进行分块,并提到了 `casync` 作为相关先例。接下来,作者阐述了如何将文件系统内容序列化为一个流,以便进行内容定义的分块。在这个过程中,作者强调了对目录条目进行广度优先遍历,以便更好地共享文件系统内容。
635 |
636 | 文章还讨论了两种分块策略的优缺点。
637 | * 一方面,让镜像定义自己的参数可以根据特定镜像进行微调,以获得更好的更新结果。
638 | * 另一方面,硬编码参数可以使镜像之间的共享更多,并使不同的镜像构建者使用相同的算法和参数。
639 |
640 | 最后,作者表示将选择留给了读者,例如哈希、参数等。这意味着读者可以根据自己的需求和偏好选择合适的技术和参数。
641 |
642 | 简单来说,这篇文章介绍了如何在不同镜像之间共享文件系统内容的技术和方法,并讨论了两种分块策略的优缺点。作者鼓励读者根据自己的需求选择最适合的分块方法。
643 |
644 |
645 | ## 内核邮件信息说明
646 |
647 | 最后,附上内核邮件的信息来结束 PuzzleFS 源码分析:
648 |
649 | PuzzleFS [1] 是一个容器文件系统,旨在解决现有 OCI 格式的一些局限性。项目的的主要目标是降低重复性,实现可重现的镜像构建,支持直接挂载以及提供内存安全保证,其中一些灵感来自 OCIv2 设计文档 [7]。
650 |
651 | 通过使用内容定义的分块算法 FastCDC,实现了降低重复性。这种实现允许分块在层之间共享。从现有层构建新层时,可以重用大部分分块。
652 |
653 | 项目的另一个目标是实现可重现的镜像构建,通过定义镜像格式的正规表示来实现。
654 |
655 | 直接挂载支持是 PuzzleFS 的关键特性之一,与 fs-verity 一起提供数据完整性。目前,PuzzleFS 作为用户空间文件系统(FUSE)实现。一个只读内核文件系统驱动正在进行中。
656 |
657 | 最后,内存安全性对 PuzzleFS 至关重要,导致决定用 Rust 实现。另一个目标是实现用户空间和内核空间共享相同代码,以提供安全的实现。
658 |
659 | * [1] https://github.com/anuvu/puzzlefs
660 | * [2] https://github.com/wedsonaf/linux/tree/fs
661 | * [3] https://github.com/Rust-for-Linux/linux/issues/1004
662 | * [4] https://github.com/Rust-for-Linux/linux/pull/1007
663 | * [5] https://docs.rs/serde_cbor/latest/serde_cbor/
664 | * [6] https://docs.rs/hex/0.4.3/hex/
665 | * [7] https://hackmd.io/@cyphar/ociv2-brainstorm
666 | * [8] https://www.youtube.com/watch?v=tPs1uRqOnlk
667 | * [9] https://github.com/Rust-for-Linux/linux/tree/rust-next
668 | * [10] https://docs.kernel.org/rust/quick-start.html
669 | * [11] https://github.com/ariel-miculas/puzzlefs/tree/support-no-verity-data
670 | * [12] https://archive.fosdem.org/2019/schedule/event/containers_atomfs/
671 |
672 | ```rust
673 | Ariel Miculas (58):
674 | rust: kernel: add libraries required by the filesystem abstractions
675 | rust: kernel: backport the delay module from the rust branch
676 | rust: kernel: add container_of macro
677 | rust: kernel: add offset_of macro
678 | drop: Add crate::pr_warn declaration
679 | rust: kernel: rename from_kernel_errno to from_errno
680 | rust: kernel: Rename from_pointer to from_foreing and into_pointer to
681 | into_foreign
682 | rust: kernel: add count_paren_items macro, needed by define_fs_params
683 | macro
684 | rust: helpers: add missing rust helper 'alloc_pages'
685 | kernel: configs: add qemu-busybox-min.config
686 | rust: kernel: format the rust code
687 | samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs
688 | kernel: configs: enable rust samples in rust.config
689 | Add SAMPLE_RUST_SERDE in rust.config
690 | rust: kernel: fix compile errors after rebase to rust-next
691 | rust: serde_cbor: import crate
692 | rust: serde_cbor: add SPDX License Identifiers
693 | rust: serde_cbor: add no_fp_fmt_parse support
694 | rust: Kbuild: enable serde_cbor
695 | samples: rust: add cbor serialize/deserialize example
696 | rust: serde_cbor: add support for serde_cbor's from_slice method by
697 | using a custom alloc_kernel feature
698 | rust: serde: add support for deserializing Vec with kernel_alloc
699 | feature
700 | rust: file: Replace UnsafeCell with Opaque for File
701 | rust: kernel: implement fmt::Debug for CString
702 | samples: puzzlefs: rename RustFs to PuzzleFs
703 | samples: puzzlefs: add basic deserializing support for the puzzlefs
704 | metadata
705 | rust: file: present the filesystem context to the open function
706 | rust: kernel: add an abstraction over vfsmount to allow cloning a new
707 | private mount
708 | rust: file: add from_path, from_path_in_root_mnt and read_with_offset
709 | methods to File
710 | samples: puzzlefs: pass the Vfsmount structure from open to read and
711 | return the contents of the data file inside /home/puzzlefs_oci
712 | rust: file: move from_path, from_path_in_root_mnt and read_with_offset
713 | methods to a RegularFile newtype
714 | rust: file: ensure RegularFile can only create regular files
715 | rust: file: add get_pos method to RegularFile
716 | rust: file: add methods read_to_end, get_file_size and update_pos to
717 | RegularFile
718 | rust: file: define a minimal Read trait and implement it for
719 | RegularFile
720 | samples: puzzlefs: add cbor_get_array_size method
721 | samples: puzzlefs: add KernelError to WireFormatError and implement
722 | From conversion
723 | samples: puzzlefs: implement new for MetadataBlob
724 | samples: puzzlefs: build puzzlefs into the kernel, thus avoiding the
725 | need to export rust symbols
726 | rust: alloc: add try_clone for Vec
727 | rust: alloc: add from_iter_fallible for Vec
728 | samples: puzzlefs: implement to_errno and from_errno for
729 | WireFormatError
730 | samples: puzzlefs: add TryReserveError (and from conversion) to
731 | WireFormatError
732 | samples: puzzlefs: add higher level inode related functionality
733 | samples: puzzlefs: populate the directory entries with the inodes from
734 | the puzzlefs metadata file
735 | rust: hex: import crate
736 | rust: hex: add SPDX license identifiers
737 | rust: Kbuild: enable `hex`
738 | rust: hex: implement FromHex trait and hex::decode using a custom
739 | kernel_alloc feature
740 | rust: hex: add encode_hex_iter and encode_hex_upper_iter methods
741 | rust: puzzlefs: add HexError to WireFormatError and implement the From
742 | conversion
743 | rust: puzzlefs: display the error value for
744 | WireFormatError::KernelError
745 | samples: puzzlefs: add Rootfs and Digest structs to types.rs
746 | samples: puzzlefs: implement the conversion from WireFormatError to
747 | kernel::error::Error
748 | rust: puzzlefs: read the puzzlefs image manifest instead of an
749 | individual metadata layer
750 | rust: puzzlefs: rename PuzzleFs to PuzzleFsModule to avoid confusion
751 | with the PuzzleFS struct
752 | rust: puzzlefs: add support for reading files
753 | rust: puzzlefs: add oci_root_dir and image_manifest filesystem
754 | parameters
755 |
756 | Miguel Ojeda (15):
757 | rust: proc-macro2: import crate
758 | rust: proc-macro2: add SPDX License Identifiers
759 | rust: proc-macro2: remove `unicode_ident` dependency
760 | rust: quote: import crate
761 | rust: quote: add SPDX License Identifiers
762 | rust: syn: import crate
763 | rust: syn: add SPDX License Identifiers
764 | rust: syn: remove `unicode-ident` dependency
765 | rust: serde: import crate
766 | rust: serde: add `no_fp_fmt_parse` support
767 | rust: serde: add SPDX License Identifiers
768 | rust: serde_derive: import crate
769 | rust: serde_derive: add SPDX License Identifiers
770 | rust: Kbuild: enable `proc-macro2`, `quote`, `syn`, `serde` and
771 | `serde_derive`
772 | rust: test `serde` support
773 |
774 | Wedson Almeida Filho (7):
775 | rust: add definitions for ref-counted inodes and dentries
776 | rust: add ability to register a file system
777 | rust: define fs context
778 | rust: add support for file system parameters
779 | rust: allow fs driver to initialise new superblocks
780 | rust: add `module_fs` macro
781 | WIP: rust: allow fs to be populated
782 |
783 | Makefile | 14 +-
784 | arch/x86/configs/qemu-busybox-min.config | 11 +
785 | kernel/configs/qemu-busybox-min.config | 56 +
786 | kernel/configs/rust.config | 11 +
787 | rust/.gitignore | 1 +
788 | rust/Makefile | 232 +-
789 | rust/alloc/vec/mod.rs | 48 +
790 | rust/bindings/bindings_helper.h | 14 +
791 | rust/bindings/lib.rs | 5 +
792 | rust/helpers.c | 76 +
793 | rust/hex/error.rs | 78 +
794 | rust/hex/lib.rs | 506 +++
795 | rust/hex/serde.rs | 104 +
796 | rust/kernel/cred.rs | 46 +
797 | rust/kernel/delay.rs | 104 +
798 | rust/kernel/driver.rs | 28 +
799 | rust/kernel/error.rs | 52 +-
800 | rust/kernel/file.rs | 1117 ++++++
801 | rust/kernel/fs.rs | 1478 ++++++++
802 | rust/kernel/fs/param.rs | 558 +++
803 | rust/kernel/io_buffer.rs | 153 +
804 | rust/kernel/iov_iter.rs | 81 +
805 | rust/kernel/lib.rs | 83 +
806 | rust/kernel/mm.rs | 149 +
807 | rust/kernel/mount.rs | 66 +
808 | rust/kernel/pages.rs | 144 +
809 | rust/kernel/str.rs | 6 +
810 | rust/kernel/test_serde.rs | 26 +
811 | rust/kernel/test_serde/de.rs | 439 +++
812 | rust/kernel/test_serde/error.rs | 73 +
813 | rust/kernel/test_serde/ser.rs | 466 +++
814 | rust/kernel/user_ptr.rs | 175 +
815 | rust/proc-macro2/detection.rs | 77 +
816 | rust/proc-macro2/fallback.rs | 1004 ++++++
817 | rust/proc-macro2/lib.rs | 1341 ++++++++
818 | rust/proc-macro2/marker.rs | 20 +
819 | rust/proc-macro2/parse.rs | 874 +++++
820 | rust/proc-macro2/rcvec.rs | 144 +
821 | rust/proc-macro2/wrapper.rs | 996 ++++++
822 | rust/quote/ext.rs | 112 +
823 | rust/quote/format.rs | 170 +
824 | rust/quote/ident_fragment.rs | 88 +
825 | rust/quote/lib.rs | 1436 ++++++++
826 | rust/quote/runtime.rs | 440 +++
827 | rust/quote/spanned.rs | 45 +
828 | rust/quote/to_tokens.rs | 211 ++
829 | rust/serde/de/format.rs | 32 +
830 | rust/serde/de/ignored_any.rs | 246 ++
831 | rust/serde/de/impls.rs | 2755 +++++++++++++++
832 | rust/serde/de/mod.rs | 2313 +++++++++++++
833 | rust/serde/de/seed.rs | 21 +
834 | rust/serde/de/utf8.rs | 48 +
835 | rust/serde/de/value.rs | 1718 ++++++++++
836 | rust/serde/integer128.rs | 84 +
837 | rust/serde/lib.rs | 351 ++
838 | rust/serde/macros.rs | 238 ++
839 | rust/serde/private/de.rs | 2997 ++++++++++++++++
840 | rust/serde/private/doc.rs | 161 +
841 | rust/serde/private/mod.rs | 52 +
842 | rust/serde/private/ser.rs | 1316 +++++++
843 | rust/serde/private/size_hint.rs | 23 +
844 | rust/serde/ser/fmt.rs | 180 +
845 | rust/serde/ser/impls.rs | 987 ++++++
846 | rust/serde/ser/impossible.rs | 218 ++
847 | rust/serde/ser/mod.rs | 1992 +++++++++++
848 | rust/serde/std_error.rs | 50 +
849 | rust/serde_cbor/de.rs | 1370 ++++++++
850 | rust/serde_cbor/error.rs | 320 ++
851 | rust/serde_cbor/lib.rs | 371 ++
852 | rust/serde_cbor/read.rs | 647 ++++
853 | rust/serde_cbor/ser.rs | 748 ++++
854 | rust/serde_cbor/tags.rs | 224 ++
855 | rust/serde_cbor/value/de.rs | 168 +
856 | rust/serde_cbor/value/mod.rs | 158 +
857 | rust/serde_cbor/value/ser.rs | 447 +++
858 | rust/serde_cbor/write.rs | 177 +
859 | rust/serde_derive/bound.rs | 408 +++
860 | rust/serde_derive/de.rs | 3148 +++++++++++++++++
861 | rust/serde_derive/dummy.rs | 46 +
862 | rust/serde_derive/fragment.rs | 76 +
863 | rust/serde_derive/internals/ast.rs | 204 ++
864 | rust/serde_derive/internals/attr.rs | 1908 +++++++++++
865 | rust/serde_derive/internals/case.rs | 199 ++
866 | rust/serde_derive/internals/check.rs | 445 +++
867 | rust/serde_derive/internals/ctxt.rs | 64 +
868 | rust/serde_derive/internals/mod.rs | 30 +
869 | rust/serde_derive/internals/receiver.rs | 287 ++
870 | rust/serde_derive/internals/respan.rs | 18 +
871 | rust/serde_derive/internals/symbol.rs | 71 +
872 | rust/serde_derive/lib.rs | 112 +
873 | rust/serde_derive/pretend.rs | 203 ++
874 | rust/serde_derive/ser.rs | 1342 ++++++++
875 | rust/serde_derive/this.rs | 34 +
876 | rust/serde_derive/try.rs | 26 +
877 | rust/syn/attr.rs | 664 ++++
878 | rust/syn/await.rs | 4 +
879 | rust/syn/bigint.rs | 68 +
880 | rust/syn/buffer.rs | 400 +++
881 | rust/syn/custom_keyword.rs | 255 ++
882 | rust/syn/custom_punctuation.rs | 302 ++
883 | rust/syn/data.rs | 495 +++
884 | rust/syn/derive.rs | 276 ++
885 | rust/syn/discouraged.rs | 196 ++
886 | rust/syn/error.rs | 430 +++
887 | rust/syn/export.rs | 41 +
888 | rust/syn/expr.rs | 3560 +++++++++++++++++++
889 | rust/syn/ext.rs | 141 +
890 | rust/syn/file.rs | 127 +
891 | rust/syn/gen/clone.rs | 2243 ++++++++++++
892 | rust/syn/gen/debug.rs | 3044 +++++++++++++++++
893 | rust/syn/gen/eq.rs | 2197 ++++++++++++
894 | rust/syn/gen/fold.rs | 3343 ++++++++++++++++++
895 | rust/syn/gen/hash.rs | 2871 ++++++++++++++++
896 | rust/syn/gen/visit.rs | 3788 +++++++++++++++++++++
897 | rust/syn/gen/visit_mut.rs | 3788 +++++++++++++++++++++
898 | rust/syn/gen_helper.rs | 156 +
899 | rust/syn/generics.rs | 1339 ++++++++
900 | rust/syn/group.rs | 284 ++
901 | rust/syn/ident.rs | 103 +
902 | rust/syn/item.rs | 3315 ++++++++++++++++++
903 | rust/syn/lib.rs | 985 ++++++
904 | rust/syn/lifetime.rs | 156 +
905 | rust/syn/lit.rs | 1602 +++++++++
906 | rust/syn/lookahead.rs | 171 +
907 | rust/syn/mac.rs | 221 ++
908 | rust/syn/macros.rs | 179 +
909 | rust/syn/op.rs | 236 ++
910 | rust/syn/parse.rs | 1316 +++++++
911 | rust/syn/parse_macro_input.rs | 181 +
912 | rust/syn/parse_quote.rs | 169 +
913 | rust/syn/pat.rs | 929 +++++
914 | rust/syn/path.rs | 856 +++++
915 | rust/syn/print.rs | 18 +
916 | rust/syn/punctuated.rs | 1070 ++++++
917 | rust/syn/reserved.rs | 46 +
918 | rust/syn/sealed.rs | 6 +
919 | rust/syn/span.rs | 69 +
920 | rust/syn/spanned.rs | 116 +
921 | rust/syn/stmt.rs | 351 ++
922 | rust/syn/thread.rs | 43 +
923 | rust/syn/token.rs | 1015 ++++++
924 | rust/syn/tt.rs | 109 +
925 | rust/syn/ty.rs | 1288 +++++++
926 | rust/syn/verbatim.rs | 35 +
927 | rust/syn/whitespace.rs | 67 +
928 | samples/rust/Kconfig | 28 +
929 | samples/rust/Makefile | 3 +
930 | samples/rust/local_data_format/de.rs | 422 +++
931 | samples/rust/local_data_format/error.rs | 73 +
932 | samples/rust/local_data_format/ser.rs | 443 +++
933 | samples/rust/puzzle.rs | 4 +
934 | samples/rust/puzzle/error.rs | 91 +
935 | samples/rust/puzzle/inode.rs | 150 +
936 | samples/rust/puzzle/oci.rs | 71 +
937 | samples/rust/puzzle/types.rs | 389 +++
938 | samples/rust/puzzle/types/cbor_helpers.rs | 50 +
939 | samples/rust/puzzlefs.rs | 220 ++
940 | samples/rust/rust_fs.rs | 105 +
941 | samples/rust/rust_serde.rs | 125 +
942 | scripts/Makefile.build | 4 +-
943 | 160 files changed, 89204 insertions(+), 29 deletions(-)
944 | create mode 100644 arch/x86/configs/qemu-busybox-min.config
945 | create mode 100644 kernel/configs/qemu-busybox-min.config
946 | create mode 100644 rust/hex/error.rs
947 | create mode 100644 rust/hex/lib.rs
948 | create mode 100644 rust/hex/serde.rs
949 | create mode 100644 rust/kernel/cred.rs
950 | create mode 100644 rust/kernel/delay.rs
951 | create mode 100644 rust/kernel/driver.rs
952 | create mode 100644 rust/kernel/file.rs
953 | create mode 100644 rust/kernel/fs.rs
954 | create mode 100644 rust/kernel/fs/param.rs
955 | create mode 100644 rust/kernel/io_buffer.rs
956 | create mode 100644 rust/kernel/iov_iter.rs
957 | create mode 100644 rust/kernel/mm.rs
958 | create mode 100644 rust/kernel/mount.rs
959 | create mode 100644 rust/kernel/pages.rs
960 | create mode 100644 rust/kernel/test_serde.rs
961 | create mode 100644 rust/kernel/test_serde/de.rs
962 | create mode 100644 rust/kernel/test_serde/error.rs
963 | create mode 100644 rust/kernel/test_serde/ser.rs
964 | create mode 100644 rust/kernel/user_ptr.rs
965 | create mode 100644 rust/proc-macro2/detection.rs
966 | create mode 100644 rust/proc-macro2/fallback.rs
967 | create mode 100644 rust/proc-macro2/lib.rs
968 | create mode 100644 rust/proc-macro2/marker.rs
969 | create mode 100644 rust/proc-macro2/parse.rs
970 | create mode 100644 rust/proc-macro2/rcvec.rs
971 | create mode 100644 rust/proc-macro2/wrapper.rs
972 | create mode 100644 rust/quote/ext.rs
973 | create mode 100644 rust/quote/format.rs
974 | create mode 100644 rust/quote/ident_fragment.rs
975 | create mode 100644 rust/quote/lib.rs
976 | create mode 100644 rust/quote/runtime.rs
977 | create mode 100644 rust/quote/spanned.rs
978 | create mode 100644 rust/quote/to_tokens.rs
979 | create mode 100644 rust/serde/de/format.rs
980 | create mode 100644 rust/serde/de/ignored_any.rs
981 | create mode 100644 rust/serde/de/impls.rs
982 | create mode 100644 rust/serde/de/mod.rs
983 | create mode 100644 rust/serde/de/seed.rs
984 | create mode 100644 rust/serde/de/utf8.rs
985 | create mode 100644 rust/serde/de/value.rs
986 | create mode 100644 rust/serde/integer128.rs
987 | create mode 100644 rust/serde/lib.rs
988 | create mode 100644 rust/serde/macros.rs
989 | create mode 100644 rust/serde/private/de.rs
990 | create mode 100644 rust/serde/private/doc.rs
991 | create mode 100644 rust/serde/private/mod.rs
992 | create mode 100644 rust/serde/private/ser.rs
993 | create mode 100644 rust/serde/private/size_hint.rs
994 | create mode 100644 rust/serde/ser/fmt.rs
995 | create mode 100644 rust/serde/ser/impls.rs
996 | create mode 100644 rust/serde/ser/impossible.rs
997 | create mode 100644 rust/serde/ser/mod.rs
998 | create mode 100644 rust/serde/std_error.rs
999 | create mode 100644 rust/serde_cbor/de.rs
1000 | create mode 100644 rust/serde_cbor/error.rs
1001 | create mode 100644 rust/serde_cbor/lib.rs
1002 | create mode 100644 rust/serde_cbor/read.rs
1003 | create mode 100644 rust/serde_cbor/ser.rs
1004 | create mode 100644 rust/serde_cbor/tags.rs
1005 | create mode 100644 rust/serde_cbor/value/de.rs
1006 | create mode 100644 rust/serde_cbor/value/mod.rs
1007 | create mode 100644 rust/serde_cbor/value/ser.rs
1008 | create mode 100644 rust/serde_cbor/write.rs
1009 | create mode 100644 rust/serde_derive/bound.rs
1010 | create mode 100644 rust/serde_derive/de.rs
1011 | create mode 100644 rust/serde_derive/dummy.rs
1012 | create mode 100644 rust/serde_derive/fragment.rs
1013 | create mode 100644 rust/serde_derive/internals/ast.rs
1014 | create mode 100644 rust/serde_derive/internals/attr.rs
1015 | create mode 100644 rust/serde_derive/internals/case.rs
1016 | create mode 100644 rust/serde_derive/internals/check.rs
1017 | create mode 100644 rust/serde_derive/internals/ctxt.rs
1018 | create mode 100644 rust/serde_derive/internals/mod.rs
1019 | create mode 100644 rust/serde_derive/internals/receiver.rs
1020 | create mode 100644 rust/serde_derive/internals/respan.rs
1021 | create mode 100644 rust/serde_derive/internals/symbol.rs
1022 | create mode 100644 rust/serde_derive/lib.rs
1023 | create mode 100644 rust/serde_derive/pretend.rs
1024 | create mode 100644 rust/serde_derive/ser.rs
1025 | create mode 100644 rust/serde_derive/this.rs
1026 | create mode 100644 rust/serde_derive/try.rs
1027 | create mode 100644 rust/syn/attr.rs
1028 | create mode 100644 rust/syn/await.rs
1029 | create mode 100644 rust/syn/bigint.rs
1030 | create mode 100644 rust/syn/buffer.rs
1031 | create mode 100644 rust/syn/custom_keyword.rs
1032 | create mode 100644 rust/syn/custom_punctuation.rs
1033 | create mode 100644 rust/syn/data.rs
1034 | create mode 100644 rust/syn/derive.rs
1035 | create mode 100644 rust/syn/discouraged.rs
1036 | create mode 100644 rust/syn/error.rs
1037 | create mode 100644 rust/syn/export.rs
1038 | create mode 100644 rust/syn/expr.rs
1039 | create mode 100644 rust/syn/ext.rs
1040 | create mode 100644 rust/syn/file.rs
1041 | create mode 100644 rust/syn/gen/clone.rs
1042 | create mode 100644 rust/syn/gen/debug.rs
1043 | create mode 100644 rust/syn/gen/eq.rs
1044 | create mode 100644 rust/syn/gen/fold.rs
1045 | create mode 100644 rust/syn/gen/hash.rs
1046 | create mode 100644 rust/syn/gen/visit.rs
1047 | create mode 100644 rust/syn/gen/visit_mut.rs
1048 | create mode 100644 rust/syn/gen_helper.rs
1049 | create mode 100644 rust/syn/generics.rs
1050 | create mode 100644 rust/syn/group.rs
1051 | create mode 100644 rust/syn/ident.rs
1052 | create mode 100644 rust/syn/item.rs
1053 | create mode 100644 rust/syn/lib.rs
1054 | create mode 100644 rust/syn/lifetime.rs
1055 | create mode 100644 rust/syn/lit.rs
1056 | create mode 100644 rust/syn/lookahead.rs
1057 | create mode 100644 rust/syn/mac.rs
1058 | create mode 100644 rust/syn/macros.rs
1059 | create mode 100644 rust/syn/op.rs
1060 | create mode 100644 rust/syn/parse.rs
1061 | create mode 100644 rust/syn/parse_macro_input.rs
1062 | create mode 100644 rust/syn/parse_quote.rs
1063 | create mode 100644 rust/syn/pat.rs
1064 | create mode 100644 rust/syn/path.rs
1065 | create mode 100644 rust/syn/print.rs
1066 | create mode 100644 rust/syn/punctuated.rs
1067 | create mode 100644 rust/syn/reserved.rs
1068 | create mode 100644 rust/syn/sealed.rs
1069 | create mode 100644 rust/syn/span.rs
1070 | create mode 100644 rust/syn/spanned.rs
1071 | create mode 100644 rust/syn/stmt.rs
1072 | create mode 100644 rust/syn/thread.rs
1073 | create mode 100644 rust/syn/token.rs
1074 | create mode 100644 rust/syn/tt.rs
1075 | create mode 100644 rust/syn/ty.rs
1076 | create mode 100644 rust/syn/verbatim.rs
1077 | create mode 100644 rust/syn/whitespace.rs
1078 | create mode 100644 samples/rust/local_data_format/de.rs
1079 | create mode 100644 samples/rust/local_data_format/error.rs
1080 | create mode 100644 samples/rust/local_data_format/ser.rs
1081 | create mode 100644 samples/rust/puzzle.rs
1082 | create mode 100644 samples/rust/puzzle/error.rs
1083 | create mode 100644 samples/rust/puzzle/inode.rs
1084 | create mode 100644 samples/rust/puzzle/oci.rs
1085 | create mode 100644 samples/rust/puzzle/types.rs
1086 | create mode 100644 samples/rust/puzzle/types/cbor_helpers.rs
1087 | create mode 100644 samples/rust/puzzlefs.rs
1088 | create mode 100644 samples/rust/rust_fs.rs
1089 | create mode 100644 samples/rust/rust_serde.rs
1090 | ```
1091 |
--------------------------------------------------------------------------------
/优秀文档参考/Rust_for_Linux驱动模块开发报告-周睿.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust for Linux驱动模块开发报告-周睿
3 | Date: 2023-11
4 | Tags:
5 | - Author: 周睿
6 | - Reports Repo: https://github.com/ZR233/learn_rust_for_linux
7 | ---
8 |
9 | # Learn Rust-for-Linux
10 |
11 | ## Day 1
12 |
13 | 今天的目标是编译并在`Qemu`上运行`kernel`。
14 | 源码相当大,有4.5G,还需要给生成留出空间,感觉至少得给20G。
15 | 编译`Qemu`
16 |
17 | ```shell
18 | sudo apt-get install git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev libaio-dev libbluetooth-dev libbrlapi-dev libbz2-dev libnfs-dev libiscsi-dev ninja-build python3 python3-pip python3-venv
19 |
20 | pip install sphinx sphinx-rtd-theme
21 |
22 |
23 | git clone git@github.com:qemu/qemu.git
24 | cd qemu
25 | mkdir build
26 | cd build
27 | ../configure --target-list=riscv64-softmmu,riscv64-linux-user,aarch64-softmmu,aarch64-linux-user,x86_64-softmmu,x86_64-linux-user
28 | make -j4
29 | sudo make install
30 | ```
31 |
32 | 编译`BusyBox`:
33 |
34 | ```shell
35 | wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
36 | tar xvf busybox-1.36.1.tar.bz2
37 | cd busybox-1.36.1/
38 | make ARCH=arm64 menuconfig -j4
39 | make ARCH=arm64 install -j4
40 |
41 | ```
42 |
43 | 基于BusyBox制作initrd
44 | 制作`initrd`步骤略多。这里`make_initrd.sh`就是用来自动制作`initrd`的脚本,内容:
45 | ```shell
46 | #!/bin/bash
47 |
48 | MOUNT_DIR=mnt
49 | CURR_DIR=`pwd`
50 |
51 | rm initrd.ext4
52 | dd if=/dev/zero of=initrd.ext4 bs=1M count=32
53 | mkfs.ext4 initrd.ext4
54 |
55 | mkdir -p $MOUNT_DIR
56 | mount initrd.ext4 $MOUNT_DIR
57 | cp -arf busybox-1.35.0/_install/* $MOUNT_DIR
58 |
59 | cd $MOUNT_DIR
60 | mkdir -p etc dev mnt proc sys tmp mnt etc/init.d/
61 |
62 | echo "proc /proc proc defaults 0 0" > etc/fstab
63 | echo "tmpfs /tmp tmpfs defaults 0 0" >> etc/fstab
64 | echo "sysfs /sys sysfs defaults 0 0" >> etc/fstab
65 |
66 | echo "#!/bin/sh" > etc/init.d/rcS
67 | echo "mount -a" >> etc/init.d/rcS
68 | echo "mount -o remount,rw /" >> etc/init.d/rcS
69 | echo "echo -e \"Welcome to ARM64 Linux\"" >> etc/init.d/rcS
70 | chmod 755 etc/init.d/rcS
71 |
72 | echo "::sysinit:/etc/init.d/rcS" > etc/inittab
73 | echo "::respawn:-/bin/sh" >> etc/inittab
74 | echo "::askfirst:-/bin/sh" >> etc/inittab
75 | chmod 755 etc/inittab
76 |
77 | cd dev
78 | mknod console c 5 1
79 | mknod null c 1 3
80 | mknod tty1 c 4 1
81 |
82 | cd $CURR_DIR
83 | umount $MOUNT_DIR
84 | echo "make initrd ok!"
85 |
86 | ```
87 |
88 | 然后必须 `sudo` 执行 `sudo ./make_inird.sh`
89 |
90 | 接着,`Qemu`启动!
91 |
92 | ```shell
93 | qemu-system-aarch64 \
94 | -nographic \
95 | -M virt \
96 | -cpu cortex-a57 \
97 | -smp 2 \
98 | -m 4G \
99 | -kernel /mnt/sdb/dev/linux/build/arch/arm64/boot \
100 | -append "nokaslr root=/dev/ram init=/linuxrc console=ttyS0" \
101 | -initrd disk.img
102 | ```
103 |
104 | 
105 |
106 | 果然第一次就失败了。似乎是文件系统没做对
107 |
108 | ## Day2
109 |
110 | 仔细查看`qemu-system-aarch64 -h`,发现指令 `-initrd file use 'file' as initial ram disk` 将文件挂载为ram,而默认`kernel`未添加`ram`支持,应该改为挂载`-hda/-hdb file use 'file' as hard disk 0/1 image`,所以运行命令修改为
111 | ```shell
112 | #!/bin/bash
113 |
114 | qemu-system-aarch64 \
115 | -nographic \
116 | -M virt \
117 | -cpu cortex-a57 \
118 | -smp 2 \
119 | -m 4G \
120 | -kernel /mnt/sdb/dev/linux/build/arch/arm64/boot/Image \
121 | -append "nokaslr root=/dev/sda init=/linuxrc console=ttyAMA0 console=ttyS0" \
122 | -hda disk.ext3
123 | ```
124 |
125 | 运行,依然报错:
126 |
127 | ```log
128 | [ 0.927382] uart-pl011 9000000.pl011: no DMA platform data
129 | [ 0.932510] /dev/root: Can't open blockdev
130 | [ 0.933619] VFS: Cannot open root device "/dev/sda" or unknown-block(0,0): error -6
131 | [ 0.933919] Please append a correct "root=" boot option; here are the available partitions:
132 | [ 0.934481] fe00 32768 vda
133 | [ 0.934649] driver: virtio_blk
134 | [ 0.934939] 1f00 131072 mtdblock0
135 | [ 0.934963] (driver?)
136 | ```
137 |
138 | 可以看出是root不对,应该改成vda:
139 |
140 | ```shell
141 | #!/bin/bash
142 |
143 | qemu-system-aarch64 \
144 | -nographic \
145 | -M virt \
146 | -cpu cortex-a57 \
147 | -smp 2 \
148 | -m 4G \
149 | -kernel /mnt/sdb/dev/linux/build/arch/arm64/boot/Image \
150 | -append "nokaslr root=/dev/vda init=/linuxrc console=ttyAMA0 console=ttyS0" \
151 | -hda disk.ext3
152 | ```
153 |
154 | 再次运行:
155 |
156 | 
157 |
158 | 成功!
159 |
160 |
161 | ## Day 3
162 |
163 | 今天写一个驱动的`helloworld`。
164 | 首先为`kernel`添加`rust-analyzer`支持。在源码根目录执行:
165 | ```shell
166 | make ARCH=arm64 LLVM=1 rust-analyzer
167 | ```
168 | 会在`build`目录生成`rust-project.json`,在`.vscode`的`settings.json`添加如下:
169 | ```json5
170 | {
171 | "rust-analyzer.linkedProjects": [
172 | "./build/rust-project.json"
173 | ]
174 | }
175 | ```
176 | 就可以为源码添加提示啦。
177 | 之后如教程所讲,添加`rust_helloworld`模块,编译后挂载文件系统,找个文件夹放进去,启动`qemu`,日志等级调成`info`: `dmesg -n 7`,加载驱动,成功。
178 |
179 | ## Day4
180 |
181 | 休息一天。
182 |
183 | ## Day5
184 |
185 | 开始做e1000驱动,首先尝试使用`fujita`的`kernel fork`,发现他的版本较低,用的`rust`版本为`1.66`,本着学习就学最新的原则,准备直接用官方最新版本修改使用。
186 | `fork`了官方版本,发现`rust`只支持`x86`,对比了`rust-dev`分支,将修改支持`arm64`的部分拷贝过来,编译并运行成功。
187 |
188 | ## Day6
189 |
190 | 对比`fujita`的分支,补全官方没有的`API`。
191 | 补了一天,发现需要改动的太多,6.6 的api也有部分变动,后面的实验也不用这个项目,所以还是老实用`fujita`的吧。
192 |
193 | ## Day7
194 |
195 | 经过前两天对内核源码的捣鼓,有了些许了解,编译`kernel`,修改代码,练习3轻松完成。
196 |
197 |
198 | ## Day8
199 |
200 | 使用`busybox`需要手动添加`/etc/network/interfaces`文件:
201 |
202 | ```
203 | auto lo
204 | iface lo inet loopback
205 | auto eth0
206 | iface eth0 inet dhcp
207 | ```
208 |
209 | 参考`e1000`驱动源码,填写练习4内容,始终无法获取网络,编译了原版`e1000`驱动测试,也不成功,看来是`qemu`配置问题。
210 |
211 | ## Day9
212 |
213 | 到处搜索资料并测试不同配置,发现问题出在没有配置本地回环地址,执行以下语句:
214 |
215 | ```shell
216 | # 添加本地回环
217 | ifconfig lo 127.0.0.1
218 | # 启动网卡
219 | ifconfig eth0 up
220 | ```
221 |
222 | 成功`ping`通。
223 |
224 |
225 | ## Day 17
226 |
227 | riscv环境配置
228 |
229 | busybox 编译
230 |
231 | ```shell
232 | make O=build_riscv ARCH=riscv64 CROSS_COMPILE=riscv64-linux-gnu- menuconfig -j4
233 |
234 | # setting 中打开 build static
235 |
236 | make O=build_riscv ARCH=riscv64 CROSS_COMPILE=riscv64-linux-gnu- install -j4
237 | ```
238 |
239 | linux 编译
240 |
241 | ```shell
242 | make LLVM=1 ARCH=riscv defconfig -j4
243 | make LLVM=1 ARCH=riscv menuconfig -j4
244 | make LLVM=1 ARCH=riscv rust-analyzer
245 | bear -- make LLVM=1 ARCH=riscv -j4
246 | ```
247 |
248 | ## Day 18
249 |
250 | 给 rust-dev 添加riscv支持:
251 | ```makefile
252 | # scripts/Makefile
253 |
254 |
255 | # 22行添加,使其生成target 配置
256 | ifdef CONFIG_RISCV
257 | always-$(CONFIG_RUST) += target.json
258 | filechk_rust_target = $< < include/config/auto.conf
259 |
260 | $(obj)/target.json: scripts/generate_rust_target include/config/auto.conf FORCE
261 | $(call filechk,rust_target)
262 | endif
263 | ```
264 |
265 | ```rust
266 | # scripts/generate_rust_target.rs
267 |
268 | else if cfg.has("RISCV") {
269 | if cfg.has("64BIT") {
270 | ts.push("arch", "riscv64");
271 | ts.push("data-layout", "e-m:e-p:64:64-i64:64-i128:128-n64-S128");
272 | ts.push("llvm-target", "riscv64-linux-gnu");
273 | ts.push("target-pointer-width", "64");
274 | } else {
275 | ts.push("arch", "riscv32");
276 | ts.push("data-layout", "e-m:e-p:32:32-i64:64-n32-S128");
277 | ts.push("llvm-target", "riscv32-linux-gnu");
278 | ts.push("target-pointer-width", "32");
279 | }
280 | ts.push("code-model", "medium");
281 | ts.push("disable-redzone", true);
282 | let mut features = "+m,+a".to_string();
283 | if cfg.has("RISCV_ISA_C") {
284 | features += ",+c";
285 | }
286 | ts.push("features", features);
287 | }
288 |
289 | ```
290 |
291 |
292 | ```Makefile
293 | # arch/riscv/Makefile
294 |
295 | KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json
296 |
297 | ```
298 |
299 | 尝试编译,成功。
300 |
--------------------------------------------------------------------------------
/优秀文档参考/Rust_for_Linux驱动模块开发报告-杨凯.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust for Linux驱动模块开发报告-杨凯
3 | Date: 2023-11
4 | Tags:
5 | - Author: 杨凯
6 | - Reports Repo: https://github.com/guevaraya/rust-for-linux/
7 | ---
8 |
9 | # 学习记录
10 |
11 | ### 问题1
12 | 代码下载,由于rust-for-linux的官方代码全镜像约为4G,国内网络想下载下来是很难的,因此需要添加--depth=1 并指定分支rust-dev,但是太慢
13 | 最终还是用下面命令完成下载:
14 |
15 | ```
16 | git clone https://github.com/fujita/linux.git -b rust-e1000
17 |
18 | ```
19 |
20 | 由于问题3,最后还是从官网下
21 | git clone -b rust-dev --depth=1
22 |
23 | ### 问题2
24 |
25 | 根据脚本 scripts/min-tool-version.sh分析
26 | rustup override set $(scripts/min-tool-version.sh rustc) 相当于 rustup override set 1.62.0
27 | cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli相当于
28 | cargo install --locked --version 0.56.0 bindgen-cli 安装
29 |
30 | ### 问题3
31 |
32 | menuconfig搞了半天发现没有找到所谓的rust support选项,经过对比config.in发现
33 |
34 | ```
35 | rust_is_available=n,
36 | cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli
37 | Blocking waiting for file lock on package cache
38 | Updating `ustc` index
39 |
40 | vierror: could not find `bindgen-cli` in registry `crates-io` with version `=0.56.0`
41 |
42 | ```
43 |
44 | 根因还是bindgen没有安装好,在crates-io官网搜索bindgen,确实最新的版本是0.65.1,但是fujita下载的版本太低,适应官网地址编译,解决了问题
45 |
46 | ```
47 | cat ~/.cargo/config
48 | [source.crates-io]
49 | registry = "https://github.com/rust-lang/crates.io-index"
50 | replace-with = 'ustc'
51 | [source.ustc]
52 | # registry = "https://mirrors.ustc.edu.cn/crates.io-index"
53 | registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
54 | [http]
55 | check-revoke = false
56 |
57 | ```
58 |
59 | 配置完成结果
60 | 
61 |
62 | ## 练习2 自定义编写Rust内核驱动模块
63 |
64 | * 1. linux目录下samples/rust,新建rust_helloworld.rs 并添加对应的Kconfig
65 | **rust_helloworld.rs**
66 |
67 |
68 | ```
69 | // SPDX-License-Identifier: GPL-2.0
70 | //! Rust minimal sample.
71 |
72 | use kernel::prelude::*;
73 |
74 | module! {
75 | type: RustHelloWorld,
76 | name: "rust_helloworld",
77 | author: "whocare",
78 | description: "hello world module in rust",
79 | license: "GPL",
80 | }
81 |
82 | struct RustHelloWorld {}
83 |
84 | impl kernel::Module for RustHelloWorld {
85 | fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result {
86 | pr_info!("Hello World from Rust module");
87 | Ok(RustHelloWorld {})
88 | }
89 | }
90 |
91 | ```
92 |
93 | * 1. samples/rust/Kconfig 配置修改如下:
94 |
95 |
96 | ```
97 | obj-$(CONFIG_SAMPLE_RUST_FS) += rust_fs.o
98 | obj-$(CONFIG_SAMPLE_RUST_SELFTESTS) += rust_selftests.o
99 | // ++++++++++++++++ add here
100 | obj-$(CONFIG_SAMPLE_RUST_HELLOWORLD) += rust_helloworld.o
101 | // ++++++++++++++++
102 | subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
103 |
104 | config SAMPLE_RUST_MINIMAL
105 | tristate "Minimal"
106 | help
107 | This option builds the Rust minimal module sample.
108 |
109 | To compile this as a module, choose M here:
110 | the module will be called rust_minimal.
111 |
112 | If unsure, say N.
113 | // +++++++++++++++ add here
114 | config SAMPLE_RUST_HELLOWORLD
115 | tristate "Print Helloworld in Rust"
116 | help
117 | This option builds the Rust HelloWorld module sample.
118 |
119 | To compile this as a module, choose M here:
120 | the module will be called rust_helloworld.
121 |
122 | If unsure, say N.
123 | // +++++++++++++++
124 | config SAMPLE_RUST_PRINT
125 | tristate "Printing macros"
126 | help
127 | This option builds the Rust printing macros sample.
128 |
129 | To compile this as a module, choose M here:
130 | the module will be called rust_print.
131 |
132 | If unsure, say N.
133 |
134 | ```
135 |
136 | 3,配置使能 run make LLVM=1 menuconfig
137 |
138 | ```
139 | Kernel hacking
140 | ---> Sample Kernel code
141 | ---> Rust samples
142 | ---> <*>Print Helloworld in Rust (NEW)
143 |
144 | ```
145 |
146 | 4,编译内核 run make LLVM=1
147 |
148 | 5, 启动QEMU,
149 |
150 | 为了方便直接在 这里下载根文件系统,解压后参照readme.txt,更换自己的内核后,运行QEMU目录:
151 |
152 | ```
153 | qemu-system-aarch64 -machine 'virt' -cpu 'cortex-a57' -m 1G -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22 -kernel ../linux/build/arch/arm64/boot/Image -initrd initrd -nographic -append "root=LABEL=rootfs console=ttyAMA0"
154 |
155 | ```
156 |
157 | ### 使用 busybox 制作内存文件系统 initramfs:
158 |
159 | 1. 下载解压 busybox 并配置环境变量
160 |
161 | ```shell
162 | wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
163 | tar -xf busybox-1.35.0.tar.bz2
164 | cd busybox-1.35.0
165 | # 配置环境变量
166 | export ARCH=arm64
167 | export CROSS_COMPILE=aarch64-linux-gnu-
168 | ```
169 |
170 | 如果教程工具没有安装,请用下面命令
171 |
172 |
173 | ```
174 | sudo apt-get install gcc-aarch64-linux-gnu
175 |
176 | ```
177 |
178 | 1. 配置编译内核的参数
179 |
180 | ```shell
181 | # busybox-1.35.0目录下
182 | make menuconfig
183 | # 修改配置,选中如下项目,静态编译
184 | # Settings -> Build Options -> [*] Build static binary (no share libs)
185 |
186 | ```
187 |
188 |
189 | 1. 编译
190 |
191 | ```shell
192 | make -j `nproc`
193 |
194 | ```
195 |
196 |
197 | 编译头文件出错,错误如下:
198 | ```
199 | make -j `nproc`
200 | SPLIT include/autoconf.h -> include/config/*
201 | GEN include/bbconfigopts.h
202 | GEN include/common_bufsiz.h
203 | GEN include/embedded_scripts.h
204 | HOSTCC applets/usage
205 | HOSTCC applets/applet_tables
206 | GEN include/usage_compressed.h
207 | GEN include/applet_tables.h include/NUM_APPLETS.h
208 | GEN include/applet_tables.h include/NUM_APPLETS.h
209 | HOSTCC applets/usage_pod
210 | CC applets/applets.o
211 | In file included from /usr/lib/gcc-cross/aarch64-linux-gnu/10/include/limits.h:195,
212 | from /usr/lib/gcc-cross/aarch64-linux-gnu/10/include/syslimits.h:7,
213 | from /usr/lib/gcc-cross/aarch64-linux-gnu/10/include/limits.h:34,
214 | from include/platform.h:157,
215 | from include/libbb.h:13,
216 | from include/busybox.h:8,
217 | from applets/applets.c:9:
218 | /usr/include/limits.h:26:10: fatal error: bits/libc-header-start.h: 没有那个文件或目录
219 | 26 | #include
220 | | ^~~~~~~~~~~~~~~~~~~~~~~~~~
221 | compilation terminated.
222 | make[1]: *** [scripts/Makefile.build:198:applets/applets.o] 错误 1
223 | make: *** [Makefile:372:applets_dir] 错误 2
224 |
225 | ```
226 |
227 | 需要安装apt install gcc-multilib
228 |
229 | ```
230 | make
231 | CC applets/applets.o
232 | In file included from /usr/include/bits/errno.h:26,
233 | from /usr/include/errno.h:28,
234 | from include/libbb.h:17,
235 | from include/busybox.h:8,
236 | from applets/applets.c:9:
237 | /usr/include/linux/errno.h:1:10: fatal error: asm/errno.h: 没有那个文件或目录
238 | 1 | #include
239 | | ^~~~~~~~~~~~~
240 | compilation terminated.
241 | make[1]: *** [scripts/Makefile.build:198:applets/applets.o] 错误 1
242 | make: *** [Makefile:372:applets_dir] 错误 2
243 |
244 | ```
245 |
246 | 解决方法:sudo ln -s /usr/i686-linux-gnu/include/asm /usr/include/asm
247 |
248 | 1. 安装
249 |
250 | 安装前 busybox-1.35.0 目录下的文件如下图所示
251 |
252 | 输入如下命令
253 |
254 |
255 | ```shell
256 | make install
257 | ```
258 |
259 |
260 | 安装后目录下生成了\_install 目录
261 |
262 |
263 | 1. 在\_install 目录下创建后续所需的文件和目录
264 |
265 | ```shell
266 | cd _install
267 | mkdir proc sys dev tmp
268 | touch init
269 | chmod +x init
270 |
271 | ```
272 |
273 | 2. 用任意的文本编辑器编辑 init 文件内容如下
274 |
275 | ```shell
276 | #!/bin/sh
277 |
278 | # 挂载一些必要的文件系统
279 | mount -t proc none /proc
280 | mount -t sysfs none /sys
281 | mount -t tmpfs none /tmp
282 | mount -t devtmpfs none /dev
283 |
284 | ```
285 |
286 |
287 | ```
288 | # 停留在控制台
289 | exec /bin/sh
290 | ```
291 |
292 |
293 |
294 | 1. 用 busybox 制作 initramfs 文件
295 |
296 | ```shell
297 | # _install目录
298 | find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz
299 |
300 | ```
301 |
302 | 执行成功后可在 busybox-1.35.0 目录下找到 initramfs.cpio.gz 文件
303 |
304 | 2. 进入 qemu 目录下,执行如下命令
305 |
306 |
307 | ```shell
308 | qemu-system-aarch64 -M virt -cpu cortex-a72 -smp 8 -m 128M -kernel (your Image path) -initrd (your initramfs.cpio.gz path) -nographic -append "init=/init console=ttyAMA0"
309 | ```
310 |
311 | 其中`(your Image path)`为上一个任务中最后的`Image`镜像文件所在的目录,`(your initramfs.cpio.gz)`为步骤 7 中执行成功后得到的 initramfs.cpio.gz 的目录,例如
312 |
313 | ```shell
314 | qemu-system-aarch64 -M virt -cpu cortex-a72 -smp 8 -m 128M -kernel /home/jun/maodou/linux/arch/arm64/boot/Image -initrd /home/jun/maodou/busybox-1.35.0/initramfs.cpio.gz -nographic -append "init=/init console=ttyAMA0"
315 | ```
316 |
317 |
318 |
319 | * 问题:failed to find romfile "efi-virtio.rom"
320 | 解决:apt-get install ipxe-qemu
321 |
322 | 
323 |
324 | [\[1\] QEMU安装启动实例](http://iric.tpddns.cn:9955/#/Rust/2023%E7%A7%8B%E5%86%AC%E8%AE%AD%E7%BB%83%E8%90%A5/%E7%AC%AC%E4%B8%89%E9%98%B6%E6%AE%B5/rust-for-linux/Qemu%E6%A8%A1%E6%8B%9F%E5%90%AF%E5%8A%A8/README)
325 |
326 | [\[2\] 编译 linux for rust 并制作 initramfs 最后编写 rust_helloworld 内核驱动 并在 qemu 环境加载](https://blog.csbxd.fun/archives/1699538198511)
327 | [通过 QEMU 打开学习 Linux kernel 的新世界](https://www.jianshu.com/p/9b68e9ea5849)
328 |
329 |
330 | ## 练习3& 4 编写bindling 和填充checkpoint编写E1000驱动
331 |
332 | 作业描述:
333 |
334 | 参考链接: https://github.com/yuoo655/e1000-driver.git
335 | ### bingding填写
336 | 在linux 的rust驱动中需要调用内核API,需要借助bindgen是一个将C 语言的头文件转化为rust源码的工具,才能调用。https://rust-lang.github.io/rust-bindgen/
337 | 下面是一个C的头文件
338 |
339 | linux/include/r4l_sample.h
340 | ```
341 | int echo_for_rust(const unsigned char * str)
342 | {
343 | printk("[r4l test]:%s\n", str);
344 | return 0;
345 | }
346 | EXPORT_SYMBOL_GPL(echo_for_rust);
347 | ```
348 | linux/rust/helpers.c
349 | ````
350 | int rust_helper_echo(const unsigned char * str)
351 | {
352 | return echo_for_rust(str);
353 | }
354 | EXPORT_SYMBOL_GPL(rust_helper_echo);
355 | ````
356 | 这样就可以在rust里面通过bingling::echo调用C代码了
357 |
358 | ### 驱动编译和文件系统制作的Makefile脚本
359 | ```
360 | all: run
361 |
362 | menuconfig:
363 | #make -C linux ARCH=arm64 LLVM=1 O=build menuconfig
364 | make -C linux-fujita ARCH=arm64 LLVM=1 O=build menuconfig
365 | build:
366 | #make -C linux/build ARCH=arm64 LLVM=1 -j4
367 | make -C linux-fujita/build ARCH=arm64 LLVM=1 -j4
368 | run:
369 | #qemu-system-aarch64 -M virt -cpu cortex-a72 -smp 8 -m 128M -kernel linux/build/arch/arm64/boot/Image -initrd busybox-1.33.2/initramfs.cpio.gz -nographic -append "init=/linuxrc console=ttyAMA0"
370 | qemu-system-aarch64 -M virt \
371 | -cpu cortex-a72 \
372 | -smp 8 \
373 | -m 128M \
374 | -device virtio-net-device,netdev=net \
375 | -netdev user,id=net,hostfwd=tcp::2222-:22 \
376 | -kernel linux-fujita/build/arch/arm64/boot/Image \
377 | -initrd busybox-1.33.2/initramfs.cpio.gz \
378 | -nographic \
379 | -device e1000,netdev=net0,bus=pcie.0 \
380 | -netdev user,id=net0 \
381 | -append "init=/linuxrc console=ttyAMA0"
382 | run_initrd:
383 | qemu-system-aarch64 -machine 'virt' \
384 | -cpu 'cortex-a57' \
385 | -m 1G -device virtio-blk-device,drive=hd \
386 | -drive file=dqib_arm64-virt/image.qcow2,if=none,id=hd \
387 | -device virtio-net-device,netdev=net \
388 | -netdev user,id=net,hostfwd=tcp::2222-:22 \
389 | -kernel linux/build/arch/arm64/boot/Image \
390 | -initrd dqib_arm64-virt/initrd \
391 | -nographic \
392 | -append "root=LABEL=rootfs console=ttyAMA0"
393 | busybox:
394 | export ARCH=arm64
395 | export CROSS_COMPILE=aarch64-linux-gnu-
396 | make -C busybox-1.33.2
397 | e1000:
398 | make -C e1000-driver/src/linux
399 | setup: e1000
400 | rm busybox-1.33.2/_install/e1000_for_linux.ko
401 | cp e1000-driver/src/e1000_for_linux.ko busybox-1.33.2/_install/
402 | cd busybox-1.33.2/; ./setup.sh; cd -
403 |
404 | ```
405 | ### 进入状态
406 |
407 | 刚开始老师讲解e1000驱动过程,云里雾里,驱动作业无从下手的感觉,前期内核编译和文件系统制作原本很简单,但是各种安装包不兼容问题,并且rust-for-linux的驱动和内核接口变化太快,导致驱动调试,e1000-driver最好还是要基于fujita-linux编译会更好一点,这块我遇到的问题是
408 |
409 | 先用官网的 https\://github.com/yuoo655/e1000-driver.git驱动的时候发现其与
410 |
411 | 随转向 编译 1.62.0 替换1.56.0 编译过去,修改了内核部分代码,花费了三四天才真正开始调驱动,看起其他同学都已经调试完成了,心里还是有点焦急的。但没办法,硬着头皮调试,不能放弃。
412 |
413 | ### 驱动调试网络原理困惑
414 |
415 | 由于之前没有接触过网卡驱动,对网卡配置有点手足无措,最后经过调查E1000有fujita的日本人已经调试好的代码参考
416 |
417 | 
418 |
419 | 驱动OSI 7层协议,应用层,表示层,会话层,传输层,网络层,链路层和物理层
420 | 物理层和链路层对应网络的驱动,应用层,表示层,会话层对应ftp,http,ssh等,而传输层就是UDP,TCP,以及最热门的QUIC协议,而链路层典型的是MAC层,物理层是Phy层,IDH等。
421 |
422 | 代码经过一系列的折腾和编译,也参考了 这位同学的代码,启动之后结果发现没有probe打印,
423 |
424 |
425 | 经过仔细对吧,我们的开机启动qemu没有加入e1000网卡参数
426 | 处理中断的时候没有考虑None的情况,导致panic
427 |
428 | ```
429 | -device e1000,netdev=net0,bus=pcie.0 \
430 | -netdev user,id=net0 \
431 |
432 | ```
433 |
434 | ### 网卡数据发送:
435 |
436 | HTTP发送网络报过程
437 |
438 | 套接字缓冲区查询命令ss -ntt
439 |
440 |
441 |
442 | 看来start_xmit从网络层项链路层发送skb_buffer数据到tx_ring
443 |
444 | skb_buffer 数据结构
445 |
446 | Ring buffer结构如下:
447 |
448 | ![Uploading image.png…]()
449 |
450 | e1000 收发参考原理:
451 |
452 | \[内核源码] Linux 网络数据接收流程(TCP)- NAPI :
453 |
454 | ### 渐入佳境-驱动debug
455 |
456 | * 问题: eth1 up的时候内核崩溃
457 |
458 | ```
459 | / # ifconfig eth1 up
460 | [ 31.261677] rust_e1000dev: Ethernet E1000 open
461 | [ 31.262660] rust_e1000dev: New E1000 device @ 0xffff800008480000
462 | [ 31.268236] rust_e1000dev: Allocated vaddr: 0xffff559803193000, paddr: 0x43193000
463 | [ 31.271569] rust_e1000dev: Allocated vaddr: 0xffff5598033d0000, paddr: 0x433d0000
464 | [ 31.282618] rust_e1000dev: Allocated vaddr: 0xffff559805c80000, paddr: 0x45c80000
465 | [ 31.289265] rust_e1000dev: Allocated vaddr: 0xffff559805d00000, paddr: 0x45d00000
466 | [ 31.292126] rust_e1000dev: e1000 CTL: 0x140240, Status: 0x80080783
467 | [ 31.293155] rust_e1000dev: e1000_init has been completed
468 | [ 31.294449] rust_e1000dev: e1000 device is initialized
469 | [ 31.302014] rust_e1000dev: handle_irq
470 | [ 31.303574] rust_e1000dev: irq::Handler E1000_ICR = 0x4
471 | [ 31.307389] rust_e1000dev: NapiPoller poll
472 | [ 31.307501] rust_e1000dev: get stats64
473 | [ 31.309985] rust_e1000dev: e1000_recv
474 | [ 31.309985]
475 | [ 31.310547] rust_kernel: panicked at 'called `Option::unwrap()` on a `None` value', /srv/dev-disk-by-uuid-acaca851-c5c0-4f99-8522-91fec8bd0740/users/yangkai/work/rcore_2023a/rust-for_linux/e1000-driver/src/linux/../e1000_for_linux.rs:86:54
476 | [ 31.321218] ------------[ cut here ]------------
477 | [ 31.322559] rust_e1000dev: get stats64
478 | [ 31.324359] kernel BUG at rust/helpers.c:48!
479 | [ 31.326392] Internal error: Oops - BUG: 00000000f2000800 [#1] PREEMPT SMP
480 | [ 31.329246] Modules linked in: e1000_for_linux(O)
481 | [ 31.335888] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G O 6.1.0-rc1-g5fc95830739f-dirty #2
482 | [ 31.340642] Hardware name: linux,dummy-virt (DT)
483 | [ 31.343687] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
484 | [ 31.346427] pc : rust_helper_BUG+0x4/0x8
485 | [ 31.350033] lr : rust_begin_unwind+0x5c/0x60
486 | [ 31.352394] sp : ffff800008003ca0
487 | [ 31.354242] x29: ffff800008003cf0 x28: ffff5598033d0000 x27: ffff800008003de0
488 | [ 31.358153] x26: ffff800008003ef0 x25: ffff800008003f00 x24: ffffb7c2d17ca5fd
489 | [ 31.361537] x23: ffffb7c31aea4634 x22: 0000000000000001 x21: 0000000000000100
490 | [ 31.364296] x20: 0000000000000000 x19: ffff800008003e00 x18: ffffffffffffffff
491 | [ 31.366870] x17: 0000000000000041 x16: ffffb7c31adfd5b8 x15: 0000000000000004
492 | [ 31.369318] x14: 0000000000000fff x13: ffffb7c31b95b090 x12: 0000000000000003
493 | [ 31.372159] x11: 00000000ffffefff x10: c0000000ffffefff x9 : 306c1c7bb7800c00
494 | / # [ 31.374389] x8 : 306c1c7bb7800c00 x7 : 205d373435303133 x6 : 332e31332020205b
495 | [ 31.378287] x5 : ffffb7c31bcf7eaf x4 : 0000000000000001 x3 : 0000000000000000
496 | [ 31.380756] x2 : 0000000000000000 x1 : ffff800008003a60 x0 : 00000000000000e3
497 | [ 31.383739] Call trace:
498 | [ 31.384726] rust_helper_BUG+0x4/0x8
499 | [ 31.386219] _RNvNtCs3yuwAp0waWO_4core9panicking9panic_fmt+0x34/0x38
500 | [ 31.389057] _RNvNtCs3yuwAp0waWO_4core9panicking5panic+0x38/0x3c
501 | [ 31.390407] _RNvXs2_CsejK4Ne9WL8c_15e1000_for_linuxNtB5_6PollerNtNtCsfATHBUcknU9_6kernel3net10NapiPoller4poll+0x560/0x564 [e1000_for_linux]
502 | [ 31.396475] _RNvMs7_NtCsfATHBUcknU9_6kernel3netINtB5_11NapiAdapterNtCsejK4Ne9WL8c_15e1000_for_linux6PollerE13poll_callbackBR_+0x44/0x58 [e1000_for_linux]
503 | [ 31.400059] __napi_poll+0x48/0x1cc
504 | [ 31.401478] net_rx_action+0x134/0x2d4
505 | [ 31.402281] __do_softirq+0xdc/0x25c
506 | [ 31.403612] ____do_softirq+0x10/0x1c
507 | [ 31.404441] call_on_irq_stack+0x2c/0x54
508 | [ 31.405413] do_softirq_own_stack+0x1c/0x28
509 | [ 31.406320] __irq_exit_rcu+0x98/0xec
510 | [ 31.407628] irq_exit_rcu+0x10/0x1c
511 | [ 31.408937] el1_interrupt+0x8c/0xbc
512 | [ 31.409942] el1h_64_irq_handler+0x18/0x24
513 | [ 31.411241] el1h_64_irq+0x64/0x68
514 | [ 31.412316] arch_cpu_idle+0x18/0x28
515 | [ 31.413007] cpuidle_idle_call+0x6c/0x1e4
516 | [ 31.413789] do_idle+0xcc/0xf4
517 | [ 31.414408] cpu_startup_entry+0x24/0x28
518 | [ 31.416156] kernel_init+0x0/0x1a0
519 | [ 31.417162] start_kernel+0x0/0x41c
520 | [ 31.418206] start_kernel+0x328/0x41c
521 | [ 31.419322] __primary_switched+0xbc/0xc4
522 | [ 31.421651] Code: a8c17bfd d50323bf d65f03c0 d503233f (d4210000)
523 | [ 31.425128] ---[ end trace 0000000000000000 ]---
524 | [ 31.427231] Kernel panic - not syncing: Oops - BUG: Fatal exception in interrupt
525 | [ 31.428962] SMP: stopping secondary CPUs
526 | [ 31.432119] Kernel Offset: 0x37c311e00000 from 0xffff800008000000
527 | [ 31.433208] PHYS_OFFSET: 0xffffaa6840000000
528 | [ 31.433966] CPU features: 0x20000,2013c080,0000421b
529 | [ 31.435508] Memory Limit: none
530 | [ 31.437214] ---[ end Kernel panic - not syncing: Oops - BUG: Fatal exception in interrupt ]---
531 |
532 | ```
533 |
534 | 边看老师的视频,参考fujita和查找网络资料,终于ping通了
535 |
536 | ```
537 | qemu-system-aarch64 -M virt -cpu cortex-a72 -smp 8 -m 128M -kernel linux/build/arch/arm64/boot/Image -initrd busybox-1.33.2/initramfs.cpio.gz -nographic -append "init=/linuxrc console=ttyAMA0"
538 | \-device virtio-net-device,netdev=net \\
539 | \-netdev user,id=net,hostfwd=tcp::2222-:22 \\
540 | \-device e1000,netdev=net0,bus=pcie.0 \\
541 | \-netdev user,id=net0 \\
542 |
543 | \[ 0.000000] Booting Linux on physical CPU 0x0000000000 \[0x410fd083]
544 |
545 | \[ 0.000000] Linux version 6.1.0-rc1-g5fc95830739f-dirty (root@openmediavault) (Debian clang version 11.0.1-2, LLD 11.0.1) #4 SMP PREEMPT Sat Nov 18 17:07:27 CST 2023
546 |
547 | \[ 0.000000] random: crng init done
548 |
549 | \[ 0.000000] Machine model: linux,dummy-virt
550 |
551 | ...
552 |
553 | / # ping 10.0.2.2
554 |
555 | PING 10.0.2.2 (10.0.2.2): 56 data bytes
556 |
557 | \[ 18.279362] \[r4l test]:binding start xmit
558 |
559 | \[ 18.281663] rust_e1000dev: Read E1000_TDT = 0x0
560 |
561 | \[ 18.282078] rust_e1000dev: >>>>>>>>> TX PKT 60
562 |
563 | \[ 18.282844] rust_e1000dev:
564 |
565 | \[ 18.282844]
566 |
567 | \[ 18.284086] rust_e1000dev: handle_irq
568 |
569 | \[ 18.291407] rust_e1000dev: irq::Handler E1000_ICR = 0x83
570 |
571 | \[ 18.293517] rust_e1000dev: NapiPoller poll
572 |
573 | \[ 18.295755] rust_e1000dev: Read E1000_RDT + 1 = 0x0
574 |
575 | \[ 18.296219] rust_e1000dev: RX PKT 64 \<\<\<\<\<\<\<\<\<
576 |
577 | \[ 18.299517] rust_e1000dev: e1000_recv
578 |
579 | \[ 18.299517]
580 |
581 | \[ 18.301622] rust_e1000dev: irq::Handler recv = 0x1
582 |
583 | \[ 18.316187] rust_e1000dev: irq::Handler recv = packets_len:0x1 datalen:0x40
584 |
585 | \[ 18.330359] \[r4l test]:binding start xmit
586 |
587 | \[ 18.332177] rust_e1000dev: Read E1000_TDT = 0x1
588 |
589 | \[ 18.332326] rust_e1000dev: >>>>>>>>> TX PKT 98
590 |
591 | \[ 18.334101] rust_e1000dev:
592 |
593 | \[ 18.334101]
594 |
595 | \[ 18.338608] rust_e1000dev: handle_irq
596 |
597 | \[ 18.340693] rust_e1000dev: irq::Handler E1000_ICR = 0x83
598 |
599 | \[ 18.346517] rust_e1000dev: NapiPoller poll
600 |
601 | \[ 18.347330] rust_e1000dev: Read E1000_RDT + 1 = 0x1
602 |
603 | \[ 18.347421] rust_e1000dev: RX PKT 98 \<\<\<\<\<\<\<\<\<
604 |
605 | \[ 18.348343] rust_e1000dev: e1000_recv
606 |
607 | \[ 18.348343]
608 |
609 | \[ 18.350169] rust_e1000dev: irq::Handler recv = 0x1
610 |
611 | \[ 18.353030] rust_e1000dev: irq::Handler recv = packets_len:0x1 datalen:0x62
612 |
613 | 64 bytes from 10.0.2.2: seq=0 ttl=255 time=122.904 ms
614 |
615 | ```
616 |
--------------------------------------------------------------------------------
/优秀文档参考/Rust_for_Linux驱动模块开发报告-江昊.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust for Linux驱动模块开发报告-江昊
3 | Date: 2023-11
4 | Tags:
5 | - Author: 江昊
6 | - Reports Repo: https://github.com/xxkeming/rust/blob/main/rust-for-linux.md
7 | ---
8 |
9 | ## 练习 1,2 作业
10 |
11 | ### 源码下载
12 | ```
13 | git clone https://github.com/Rust-for-Linux/linux -b rust-dev --depth 1
14 | git clone https://github.com/fujita/linux.git -b rust-e1000 --depth 1
15 | ```
16 |
17 | ### 环境搭建
18 | ```
19 | ## Install dependency packages
20 | sudo apt-get -y install \
21 | binutils build-essential libtool texinfo \
22 | gzip zip unzip patchutils curl git \
23 | make cmake ninja-build automake bison flex gperf \
24 | grep sed gawk bc \
25 | zlib1g-dev libexpat1-dev libmpc-dev \
26 | libglib2.0-dev libfdt-dev libpixman-1-dev libelf-dev libssl-dev
27 |
28 | ## Install the LLVM
29 | sudo apt-get install clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang
30 |
31 | ## Add Rust environment
32 | cd linux
33 | rustup override set $(scripts/min-tool-version.sh rustc)
34 | rustup component add rust-src
35 | cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli
36 | make LLVM=1 rustavailable
37 | ```
38 |
39 | ### 编译时遇到的问题 bindgen版本过高,修改了部分增对bindgen参数的Makefile配置
40 |
41 |
42 | ### 编译流程,配置开启rust,并把练习2的代码及配置加入
43 | ```
44 | make ARCH=arm64 LLVM=1 O=build defconfig
45 |
46 | make ARCH=arm64 LLVM=1 O=build menuconfig
47 |
48 | cd build
49 | make ARCH=arm64 LLVM=1 -j4
50 | ```
51 |
52 | ### 启动方式
53 | ```
54 | qemu-system-aarch64 -machine 'virt' -cpu 'cortex-a57' -m 1G -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22 -kernel /home/parallels/linux/build/arch/arm64/boot/Image.gz -initrd initrd -nographic -append "root=LABEL=rootfs console=ttyAMA0"
55 |
56 | ```
57 |
58 | ### 运行结果
59 |
60 |
61 |
62 | ## 练习 3,4 作业
63 | ### 网络环境配置
64 | ```
65 | 主机配置:
66 | sudo ip link add br0 type bridge
67 | sudo ip addr add 192.168.100.50/24 brd 192.168.100.255 dev br0
68 | sudo ip tuntap add mode tap user $(whoami)
69 | ip tuntap show
70 | sudo ip link set tap0 master br0
71 | sudo ip link set dev br0 up
72 | sudo ip link set dev tap0 up
73 |
74 | 虚拟机配置:
75 | ifconfig lo 127.0.0.1 netmask 255.0.0.0 up
76 | ifconfig eth0 192.168.100.224 netmask 255.255.255.0 broadcast 192.168.100.255 up
77 |
78 | qemu启动方式:
79 | qemu-system-aarch64 -M virt -cpu cortex-a57 -smp 1 -m 256M -kernel build/arch/arm64/boot/Image.gz -initrd ../busybox-1.36.1/initramfs.cpio.gz -nographic -vga none -no-reboot -append "init=/init console=ttyAMA0" -netdev tap,ifname=tap0,id=tap0,script=no,downscript=no -device e1000,netdev=tap0
80 | ```
81 | ### 添加自定义函数
82 | ```
83 | 1.直接在rust/helpers.c添加自定义函数void rust_helper_test(void)
84 | 2.在rust/bindings/lib.rs对bindings_helper添加pub
85 | 3.调用方式:kernel::bindings::bindings_helper::test()
86 | ```
87 | ### e100网卡驱动作业仓库地址及运行结果
88 | ```
89 | https://github.com/xxkeming/e1000-driver
90 | ```
91 |
92 |
93 | ## 补充: 自定义基本模块
94 | ### test.rs
95 | ```
96 | use kernel::prelude::*;
97 | module! {
98 | type: RustTest,
99 | name: "rust_test",
100 | author: "keming",
101 | description: "test module in rust",
102 | license: "GPL",
103 | }
104 | struct RustTest {}
105 | impl kernel::Module for RustTest {
106 | fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result {
107 | pr_info!("test from Rust module");
108 | Ok(RustTest {})
109 | }
110 | }
111 | ```
112 | ### makefile
113 | ```
114 | obj-m := test.o
115 | PWD := $(shell pwd)
116 | ARCH ?= arm64
117 | KDIR ?= /lib/modules/$(shell uname -r)/build
118 | default:
119 | $(MAKE) ARCH=$(ARCH) LLVM=1 -C $(KDIR) M=$(PWD)/ modules
120 | clean:
121 | $(MAKE) ARCH=$(ARCH) LLVM=1 -C $(KDIR) M=$(PWD)/ clean
122 | ```
123 |
--------------------------------------------------------------------------------
/优秀文档参考/Rust_for_Linux驱动模块开发报告-童浩轩.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust for Linux驱动模块开发报告-童浩轩
3 | Date: 2023-11
4 | Tags:
5 | - Author: 童浩轩
6 | - Reports Repo: https://github.com/Tunghohin/rust-for-linux/tree/main/reports
7 | ---
8 |
9 | ## 编译 linux
10 |
11 | #### 1. 获取源码
12 |
13 | ```sh
14 | git clone https://github.com/Rust-for-Linux/linux -b rust-dev --depth=1
15 | ```
16 |
17 | #### 2. 安装依赖
18 |
19 | - 获取依赖包
20 |
21 | ```sh
22 | sudo apt-get -y install \
23 | binutils build-essential libtool texinfo \
24 | gzip zip unzip patchutils curl git \
25 | make cmake ninja-build automake bison flex gperf \
26 | grep sed gawk bc \
27 | zlib1g-dev libexpat1-dev libmpc-dev \
28 | libglib2.0-dev libfdt-dev libpixman-1-dev libelf-dev libssl-dev
29 | ```
30 |
31 | - 获取 clang 和 llvm
32 |
33 | ```sh
34 | sudo apt install clang llvm
35 | ```
36 |
37 | #### 3. 编译 linux 内核
38 |
39 | ```sh
40 | make ARCH=arm64 LLVM=1 defconfig
41 | make ARCH=arm64 LLVM=1 menuconfig # general setup 里打开 rust support
42 | cd ./build
43 | bear -- make ARCH=arm64 LLVM=1 -j12 # 顺便生成 compile_commands.json
44 | ```
45 |
46 | #### 4. 获取 dqib 系统镜像
47 |
48 | ```sh
49 | https://people.debian.org/~gio/dqib/
50 | ```
51 |
52 | 使用脚本运行 qemu
53 |
54 | ```sh
55 | #!/bin/bash
56 |
57 | qemu-system-aarch64 \
58 | -machine 'virt' \
59 | -cpu 'cortex-a57' \
60 | -m 1G \
61 | -device virtio-blk-device,drive=hd \
62 | -drive file=image.qcow2,if=none,id=hd \
63 | -device virtio-net-device,netdev=net \
64 | -netdev user,id=net,hostfwd=tcp::2222-:22 \
65 | -kernel /home/notaroo/kernel_dev/linux-rust/build/arch/arm64/boot/Image.gz \
66 | -initrd initrd \
67 | -append "root=LABEL=rootfs console=ttyAMA0"
68 | ```
69 |
70 | ## 运行结果
71 |
72 |
73 |
74 | ## 加载模块
75 |
76 | 将 .ko 拷贝到制作好的 rootfs 里
77 | 运行
78 |
79 | ```sh
80 | insmod rust_hello.ko
81 | ```
82 |
83 | ## 碰到的问题
84 |
85 | #### 1. 根文件系统构建
86 |
87 | 安装 debootstrap 和 qemu-utils
88 |
89 | ```sh
90 | sudo apt-get install debootstrap qemu-utils
91 | ```
92 |
93 | 创建磁盘镜像文件格式化并挂载
94 |
95 | ```sh
96 | qemu-img create -f raw rootfs.img 4G
97 | mkfs.ext4 rootfs.img
98 | sudo mkdir /mnt/rootfs
99 | sudo mount -o loop rootfs.img /mnt/rootfs
100 | ```
101 |
102 | 安装 debian 镜像
103 |
104 | ```sh
105 | sudo debootstrap --arch arm64 stable /mnt/rootfs https://mirrors.tuna.tsinghua.edu.cn/debian/
106 | ```
107 |
108 | 运行 `run.sh`
109 |
110 | #### 2. rust-analyzer
111 |
112 | ```sh
113 | make LLVM=1 ARCH=arm64 rust-analyzer
114 | ```
115 |
116 | #### 3. deboostrap 下载慢
117 |
118 | 加上清华的源
119 |
120 | ```
121 | https://mirrors.tuna.tsinghua.edu.cn/debian/
122 | ```
123 |
124 | #### 4. 根文件系统挂载不上
125 |
126 | 在根文件创建 /dev/vda,在 qemu 中指定 root=/dev/vda rw
127 |
128 | #### 5. qemu 连不上网
129 |
130 | 配置 `/etc/network/interfaces`
131 |
132 | ```
133 | auto eth0
134 | allow-hotplug eth0
135 | iface eth0 inet dhcp
136 | ```
137 |
138 | 同时在 qemu 里根文件指定路径后添加 rw
139 |
140 | ## 运行结果
141 |
142 |
143 |
144 | ## binding
145 |
146 | #### 1.在 include/linux 中创建文件
147 |
148 |
149 |
150 | #### 2.在 rust/bindings/bindings_helper.h 中 include 头文件,并将其于 /rust/helpers.c 中 export
151 |
152 |
153 |
154 |
155 | #### 3.通过 rust 模块调用
156 |
157 |
158 |
159 | ## 要点
160 |
161 | #### dma
162 |
163 | DMA(Direct Memory Access)可以使得外部设备可以不用 CPU 干预,直接把数据传输到内存,这样可以解放 CPU,提高系统性能
164 |
165 | 使用 dma_alloc_coherent/dma_free_coherent,分配/释放网卡 tx_ring 和 rx_ring 的 dma 空间
166 |
167 | #### napi
168 |
169 | 由于当网络包首发密集时,频繁地触发中断会严重影响 cpu 的执行效率,napi 是 linux 上采用的以提高网络处理效率的技术。
170 | 网卡接收到数据,通过硬中断通知 CPU 进行处理,但是当网卡有大量数据涌入时,频繁中断消耗大量的 CPU 处理时间在中断自身处理上,使得网卡和 CPU 工作效率低下,所以系统采用了硬中断进入轮询队列 + 软中断退出轮询队列技术,提升数据接收处理效率
171 |
172 | #### ring buffer
173 |
174 | e1000 网卡环形缓冲区,通过 DMA 映射到内存
175 |
176 | #### sk_buff
177 |
178 | 网络是分层的,对于应用层而言不用关心具体的底层是如何工作的,只需要按照协议将要发送或接收的数据打包成 packet 即可。
179 |
180 | 要使用 sk_buff 必须先分配
181 | 使用 alloc_skb_ip_align 分配一个 sk_buff
182 |
183 | 接收数据时,将网卡 rx_ring 受到的 packet 写入到 sk_buff
184 | 发送数据时,将 sk_buff 的数据使用 e1000_transmit 发送到网卡的 tx_ring
185 |
186 | #### 参考资料
187 |
188 | https://www.zhihu.com/people/wenfh2020/posts?page=2
189 |
190 | https://stackoverflow.com/questions/47450231/what-is-the-relationship-of-dma-ring-buffer-and-tx-rx-ring-for-a-network-card?answertab=votes#tab-top
191 |
192 | https://github.com/fujita/rust-e1000
193 |
194 | https://elixir.bootlin.com/linux/v4.19.121/source/drivers/net/ethernet/intel/e1000
195 |
--------------------------------------------------------------------------------
/优秀文档参考/Rust_for_Linux驱动模块开发报告-邝劲强.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust for Linux驱动模块开发报告-邝劲强
3 | Date: 2023-11
4 | Tags:
5 | - Author: 邝劲强
6 | - Reports Repo: https://github.com/lispking/rust-for-linux/tree/main/reports
7 | ---
8 |
9 | # Exercise 1
10 | ## 练习1: Rust for Linux 仓库源码获取,编译环境部署,及Rust内核编译。之后尝试在模拟器Qemu上运行起来。
11 |
12 | ### 以下是整个练习过程使用到的命令及相关截图结果:
13 |
14 | 因为 `Rust-for-Linux/linux` 仓库下载确实太慢,所以,这次实验使用仓库是 `fujita/linux`:
15 |
16 | ```shell
17 | git clone git@github.com:fujita/linux.git -b rust-e1000 --depth 1 linux-rust-e1000
18 | ```
19 |
20 | > 注:这里用了两个小技巧:
21 | > 1. 使用 `git` 协议下载,可以非常稳定将代码下载到本地
22 | > 2. 不需要关注历史 commit 信息,加上 `--depth 1`命令
23 |
24 |
25 | ### 0. 环境准备
26 |
27 | #### 环境说明
28 |
29 | * 机器配置: `CPU:M1;内存:16G;磁盘:256G`
30 | * 主机OS:`macOS Sonoma 14.0`
31 | * 虚拟机:`Ubuntu 23.04`
32 |
33 | #### 环境依赖
34 |
35 | ```shell
36 | ## first Update
37 | sudo apt-get update
38 |
39 | ## Install dependency packages
40 | sudo apt-get -y install \
41 | binutils build-essential libtool texinfo \
42 | gzip zip unzip patchutils curl git \
43 | make cmake ninja-build automake bison flex gperf \
44 | grep sed gawk bc \
45 | zlib1g-dev libexpat1-dev libmpc-dev \
46 | libglib2.0-dev libfdt-dev libpixman-1-dev libelf-dev libssl-dev
47 |
48 | ## Install the LLVM
49 | sudo apt-get install clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang
50 |
51 | ## Add Rust environment
52 | cd linux
53 | make LLVM=1 rustavailable
54 | rustup override set $(scripts/min-tool-version.sh rustc)
55 | rustup component add rust-src
56 |
57 | ## Rust-for-Linux/linux 官方 bindgen 版本较新,需将命令改成 bindgen-cli
58 | cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen
59 | ```
60 |
61 | > 小技巧:若本地已经安装过 `bindgen`,在执行 `make LLVM=1 rustavailable` 会提示版本有差异,可以使用 `--force` 命令强制安装当前 `linux` 源码需要用到的版本。
62 |
63 | ### 1. 生成默认配置
64 |
65 | ```shell
66 | make ARCH=arm64 LLVM=1 O=build defconfig
67 | ```
68 |
69 | 
70 |
71 |
72 | ### 2. 支持 Rust 选项开启
73 | ```shell
74 | make ARCH=arm64 LLVM=1 O=build menuconfig
75 | ```
76 |
77 | #### 2.1 进入配置页面,按回车选择 `General setup`
78 |
79 | 
80 |
81 | #### 2.2 滚动到最下方,按空格选择 `Rust support`,然后,按 `ESC` 返回上一层
82 |
83 | 
84 |
85 | #### 2.3 再按一次 `ESC` 弹窗,按回车确认 `Yes` 即可保存配置
86 | 
87 |
88 | #### 2.4 配置保存结果如下图所示:
89 |
90 | 
91 |
92 | ### 3. 编译内核
93 |
94 | ```shell
95 | cd build && time make ARCH=arm64 LLVM=1 -j8
96 | ```
97 |
98 | > 编译结果如下图所示,代表编译成功(整体时间花费约 `半小时`),若编译出错,最后会出现 `Error` 字眼
99 |
100 | 
101 |
102 |
103 | ### 4. 进入 `Qemu` 环境验证
104 |
105 | #### 4.1 准备 `busybox` 工具
106 |
107 | ```shell
108 | ## 下载,写作时,最新版本是 busybox-1.36.1
109 | wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
110 | ## 解压
111 | tar xjf busybox-1.36.1.tar.bz2
112 | ## 进入目录
113 | cd busybox-1.36.1
114 | ## 开启静态二进制,架构用 arm64
115 | make menuconfig ARCH=arm64
116 | ```
117 |
118 | > 进入 `busybox` 菜单选择页,在 `Settings` 按回车进入下一个页面
119 |
120 | 
121 |
122 | > 找到 `Build static binary (no shared libs)` 选项,并按空格选中后,按 `ESC` 返回上层,再按一次进入提示保存页面,回车选择 `Yes` 保存即完成静态二进制编译支持。
123 |
124 | 
125 |
126 |
127 | > 编译 busybox 二进制文件,并将该执行文件 copy 到 linux 源码目录
128 |
129 | ```shell
130 | ## 编译 busybox,编译成功后,会生成 busybox 可执行文件在 busybox-1.36.1 目录下
131 | make -j8
132 | ## 拷贝文件到 rust-for-linux 工程项目根目录
133 | cp /tmp/busybox-1.36.1/busybox $HOME/linux/
134 | ```
135 |
136 | #### 4.2 开启 `Rust Samples`
137 |
138 | * 按 [2. 支持 Rust 选项开启](#2-支持-rust-选项开启) 步骤,重新进入 `menuconfig`,开启所有 `Rust Samples`
139 |
140 | * 增加 `rust_module_parameters` 模块,并将文件名及源码内容作如下修改
141 |
142 | ```shell
143 | cd $HOME/linux
144 |
145 | cp samples/rust/rust_module_parameters.rs samples/rust/rust_module_parameters_builtin_default.rs
146 | cp samples/rust/rust_module_parameters.rs samples/rust/rust_module_parameters_builtin_custom.rs
147 | cp samples/rust/rust_module_parameters.rs samples/rust/rust_module_parameters_loadable_default.rs
148 | cp samples/rust/rust_module_parameters.rs samples/rust/rust_module_parameters_loadable_custom.rs
149 |
150 | sed -i 's:rust_module_parameters:rust_module_parameters_builtin_default:g' samples/rust/rust_module_parameters_builtin_default.rs
151 | sed -i 's:rust_module_parameters:rust_module_parameters_builtin_custom:g' samples/rust/rust_module_parameters_builtin_custom.rs
152 | sed -i 's:rust_module_parameters:rust_module_parameters_loadable_default:g' samples/rust/rust_module_parameters_loadable_default.rs
153 | sed -i 's:rust_module_parameters:rust_module_parameters_loadable_custom:g' samples/rust/rust_module_parameters_loadable_custom.rs
154 |
155 | echo 'obj-y += rust_module_parameters_builtin_default.o' >> samples/rust/Makefile
156 | echo 'obj-y += rust_module_parameters_builtin_custom.o' >> samples/rust/Makefile
157 | echo 'obj-m += rust_module_parameters_loadable_default.o' >> samples/rust/Makefile
158 | echo 'obj-m += rust_module_parameters_loadable_custom.o' >> samples/rust/Makefile
159 | ```
160 | * 按 [3. 编译内核](#3-编译内核) 命令,将 `Rust Samples` 源码编译成 `.ko` 文件
161 |
162 | #### 4.3 生成 `qemu-initramfs.img` 镜像
163 |
164 | ```shell
165 | sed -i 's:samples/rust/:build/samples/rust/:' .github/workflows/qemu-initramfs.desc
166 |
167 | build/usr/gen_init_cpio .github/workflows/qemu-initramfs.desc > qemu-initramfs.img
168 | ```
169 |
170 | #### 4.4 执行 `qemu` 命令验证结果
171 |
172 | ```shell
173 | qemu-system-aarch64 \
174 | -kernel build/arch/arm64/boot/Image.gz \
175 | -initrd qemu-initramfs.img \
176 | -M virt \
177 | -cpu cortex-a72 \
178 | -smp 2 \
179 | -nographic \
180 | -vga none \
181 | -no-reboot \
182 | -append ' \
183 | rust_module_parameters_builtin_custom.my_bool=n \
184 | rust_module_parameters_builtin_custom.my_i32=345543 \
185 | rust_module_parameters_builtin_custom.my_str=🦀mod \
186 | rust_module_parameters_builtin_custom.my_usize=84 \
187 | rust_module_parameters_builtin_custom.my_array=1,2,3 \
188 | ' \
189 | | sed 's:\r$::'
190 | ```
191 |
192 | > 若执行结果如下所示,则代表内核准备一切就绪,然后,就可以愉快地开始编写内核驱动了。
193 |
194 | ```shell
195 | [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd083]
196 | [ 0.000000] Linux version 6.1.0-rc1-gbb59bfc550bc-dirty (lispking@ubuntu) (Ubuntu clang version 15.0.7, Ubuntu LLD 15.0.7) #4 SMP PREEMPT Tue Nov 7 00:39:03 CST 2023
197 | [ 0.000000] random: crng init done
198 | [ 0.000000] Machine model: linux,dummy-virt
199 | [ 0.000000] efi: UEFI not found.
200 | [ 0.000000] NUMA: No NUMA configuration found
201 | [ 0.000000] NUMA: Faking a node at [mem 0x0000000040000000-0x0000000047ffffff]
202 | [ 0.000000] NUMA: NODE_DATA [mem 0x47fb0a00-0x47fb2fff]
203 | [ 0.000000] Zone ranges:
204 | [ 0.000000] DMA [mem 0x0000000040000000-0x0000000047ffffff]
205 | [ 0.000000] DMA32 empty
206 | [ 0.000000] Normal empty
207 | [ 0.000000] Movable zone start for each node
208 | [ 0.000000] Early memory node ranges
209 | [ 0.000000] node 0: [mem 0x0000000040000000-0x0000000047ffffff]
210 | [ 0.000000] Initmem setup node 0 [mem 0x0000000040000000-0x0000000047ffffff]
211 | [ 0.000000] cma: Reserved 32 MiB at 0x0000000045c00000
212 | [ 0.000000] psci: probing for conduit method from DT.
213 | [ 0.000000] psci: PSCIv1.1 detected in firmware.
214 | [ 0.000000] psci: Using standard PSCI v0.2 function IDs
215 | [ 0.000000] psci: Trusted OS migration not required
216 | [ 0.000000] psci: SMC Calling Convention v1.0
217 | [ 0.000000] percpu: Embedded 20 pages/cpu s44840 r8192 d28888 u81920
218 | [ 0.000000] Detected PIPT I-cache on CPU0
219 | [ 0.000000] CPU features: detected: Spectre-v2
220 | [ 0.000000] CPU features: detected: Spectre-v3a
221 | [ 0.000000] CPU features: detected: Spectre-v4
222 | [ 0.000000] CPU features: detected: Spectre-BHB
223 | [ 0.000000] CPU features: kernel page table isolation forced ON by KASLR
224 | [ 0.000000] CPU features: detected: Kernel page table isolation (KPTI)
225 | [ 0.000000] CPU features: detected: ARM erratum 1742098
226 | [ 0.000000] CPU features: detected: ARM errata 1165522, 1319367, or 1530923
227 | [ 0.000000] alternatives: applying boot alternatives
228 | [ 0.000000] Fallback order for Node 0: 0
229 | [ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 32256
230 | [ 0.000000] Policy zone: DMA
231 | [ 0.000000] Kernel command line: \
232 | [ 0.000000] rust_module_parameters_builtin_custom.my_bool=n \
233 | [ 0.000000] rust_module_parameters_builtin_custom.my_i32=345543 \
234 | [ 0.000000] rust_module_parameters_builtin_custom.my_str=🦀mod \
235 | [ 0.000000] rust_module_parameters_builtin_custom.my_usize=84 \
236 | [ 0.000000] rust_module_parameters_builtin_custom.my_array=1,2,3 \
237 | [ 0.000000]
238 | [ 0.000000] Unknown kernel command line parameters "\ \ \ \ \ \", will be passed to user space.
239 | [ 0.000000] Dentry cache hash table entries: 16384 (order: 5, 131072 bytes, linear)
240 | [ 0.000000] Inode-cache hash table entries: 8192 (order: 4, 65536 bytes, linear)
241 | [ 0.000000] mem auto-init: stack:all(zero), heap alloc:off, heap free:off
242 | [ 0.000000] Memory: 58068K/131072K available (16640K kernel code, 3724K rwdata, 9340K rodata, 1920K init, 610K bss, 40236K reserved, 32768K cma-reserved)
243 | [ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=2, Nodes=1
244 | [ 0.000000] rcu: Preemptible hierarchical RCU implementation.
245 | [ 0.000000] rcu: RCU event tracing is enabled.
246 | [ 0.000000] rcu: RCU restricting CPUs from NR_CPUS=256 to nr_cpu_ids=2.
247 | [ 0.000000] Trampoline variant of Tasks RCU enabled.
248 | [ 0.000000] Tracing variant of Tasks RCU enabled.
249 | [ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
250 | [ 0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=2
251 | [ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
252 | [ 0.000000] Root IRQ handler: gic_handle_irq
253 | [ 0.000000] GICv2m: range[mem 0x08020000-0x08020fff], SPI[80:143]
254 | [ 0.000000] rcu: srcu_init: Setting srcu_struct sizes based on contention.
255 | [ 0.000000] arch_timer: cp15 timer(s) running at 62.50MHz (virt).
256 | [ 0.000000] clocksource: arch_sys_counter: mask: 0x1ffffffffffffff max_cycles: 0x1cd42e208c, max_idle_ns: 881590405314 ns
257 | [ 0.000034] sched_clock: 57 bits at 63MHz, resolution 16ns, wraps every 4398046511096ns
258 | [ 0.003104] Console: colour dummy device 80x25
259 | [ 0.006498] printk: console [tty0] enabled
260 | [ 0.007580] Calibrating delay loop (skipped), value calculated using timer frequency.. 125.00 BogoMIPS (lpj=250000)
261 | [ 0.007696] pid_max: default: 32768 minimum: 301
262 | [ 0.008201] LSM: Security Framework initializing
263 | [ 0.009621] Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
264 | [ 0.009667] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
265 | [ 0.027385] cacheinfo: Unable to detect cache hierarchy for CPU 0
266 | [ 0.030707] cblist_init_generic: Setting adjustable number of callback queues.
267 | [ 0.030813] cblist_init_generic: Setting shift to 1 and lim to 1.
268 | [ 0.031032] cblist_init_generic: Setting shift to 1 and lim to 1.
269 | [ 0.031887] rcu: Hierarchical SRCU implementation.
270 | [ 0.031927] rcu: Max phase no-delay instances is 1000.
271 | [ 0.034754] EFI services will not be available.
272 | [ 0.035301] smp: Bringing up secondary CPUs ...
273 | [ 0.038000] Detected PIPT I-cache on CPU1
274 | [ 0.038422] cacheinfo: Unable to detect cache hierarchy for CPU 1
275 | [ 0.038656] CPU1: Booted secondary processor 0x0000000001 [0x410fd083]
276 | [ 0.040629] smp: Brought up 1 node, 2 CPUs
277 | [ 0.041139] SMP: Total of 2 processors activated.
278 | [ 0.041197] CPU features: detected: 32-bit EL0 Support
279 | [ 0.041224] CPU features: detected: 32-bit EL1 Support
280 | [ 0.041273] CPU features: detected: CRC32 instructions
281 | [ 0.042832] CPU: All CPU(s) started at EL1
282 | [ 0.042955] alternatives: applying system-wide alternatives
283 | [ 0.053178] devtmpfs: initialized
284 | [ 0.060754] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
285 | [ 0.060882] futex hash table entries: 512 (order: 3, 32768 bytes, linear)
286 | [ 0.063074] pinctrl core: initialized pinctrl subsystem
287 | [ 0.069593] DMI not present or invalid.
288 | [ 0.097328] NET: Registered PF_NETLINK/PF_ROUTE protocol family
289 | [ 0.103269] DMA: preallocated 128 KiB GFP_KERNEL pool for atomic allocations
290 | [ 0.103577] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA pool for atomic allocations
291 | [ 0.103741] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations
292 | [ 0.103955] audit: initializing netlink subsys (disabled)
293 | [ 0.104990] audit: type=2000 audit(0.064:1): state=initialized audit_enabled=0 res=1
294 | [ 0.107444] thermal_sys: Registered thermal governor 'step_wise'
295 | [ 0.107475] thermal_sys: Registered thermal governor 'power_allocator'
296 | [ 0.107695] cpuidle: using governor menu
297 | [ 0.108455] hw-breakpoint: found 6 breakpoint and 4 watchpoint registers.
298 | [ 0.108858] ASID allocator initialised with 32768 entries
299 | [ 0.111764] Serial: AMBA PL011 UART driver
300 | [ 0.133623] 9000000.pl011: ttyAMA0 at MMIO 0x9000000 (irq = 13, base_baud = 0) is a PL011 rev1
301 | [ 0.138161] printk: console [ttyAMA0] enabled
302 | [ 0.144714] KASLR enabled
303 | [ 0.172047] HugeTLB: registered 1.00 GiB page size, pre-allocated 0 pages
304 | [ 0.172209] HugeTLB: 16380 KiB vmemmap can be freed for a 1.00 GiB page
305 | [ 0.172317] HugeTLB: registered 32.0 MiB page size, pre-allocated 0 pages
306 | [ 0.172405] HugeTLB: 508 KiB vmemmap can be freed for a 32.0 MiB page
307 | [ 0.172490] HugeTLB: registered 2.00 MiB page size, pre-allocated 0 pages
308 | [ 0.172588] HugeTLB: 28 KiB vmemmap can be freed for a 2.00 MiB page
309 | [ 0.172694] HugeTLB: registered 64.0 KiB page size, pre-allocated 0 pages
310 | [ 0.172794] HugeTLB: 0 KiB vmemmap can be freed for a 64.0 KiB page
311 | [ 0.178560] ACPI: Interpreter disabled.
312 | [ 0.182045] iommu: Default domain type: Translated
313 | [ 0.182153] iommu: DMA domain TLB invalidation policy: strict mode
314 | [ 0.183245] SCSI subsystem initialized
315 | [ 0.184691] usbcore: registered new interface driver usbfs
316 | [ 0.184938] usbcore: registered new interface driver hub
317 | [ 0.185193] usbcore: registered new device driver usb
318 | [ 0.186663] pps_core: LinuxPPS API ver. 1 registered
319 | [ 0.186741] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti
320 | [ 0.186899] PTP clock support registered
321 | [ 0.187237] EDAC MC: Ver: 3.0.0
322 | [ 0.191035] FPGA manager framework
323 | [ 0.191539] Advanced Linux Sound Architecture Driver Initialized.
324 | [ 0.197068] vgaarb: loaded
325 | [ 0.201654] clocksource: Switched to clocksource arch_sys_counter
326 | [ 0.203078] VFS: Disk quotas dquot_6.6.0
327 | [ 0.203335] VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
328 | [ 0.204321] pnp: PnP ACPI: disabled
329 | [ 0.233969] NET: Registered PF_INET protocol family
330 | [ 0.235788] IP idents hash table entries: 2048 (order: 2, 16384 bytes, linear)
331 | [ 0.240230] tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes, linear)
332 | [ 0.240647] Table-perturb hash table entries: 65536 (order: 6, 262144 bytes, linear)
333 | [ 0.240810] TCP established hash table entries: 1024 (order: 1, 8192 bytes, linear)
334 | [ 0.241319] TCP bind hash table entries: 1024 (order: 3, 32768 bytes, linear)
335 | [ 0.241642] TCP: Hash tables configured (established 1024 bind 1024)
336 | [ 0.242672] UDP hash table entries: 256 (order: 1, 8192 bytes, linear)
337 | [ 0.243137] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes, linear)
338 | [ 0.244082] NET: Registered PF_UNIX/PF_LOCAL protocol family
339 | [ 0.246729] RPC: Registered named UNIX socket transport module.
340 | [ 0.246978] RPC: Registered udp transport module.
341 | [ 0.247087] RPC: Registered tcp transport module.
342 | [ 0.247207] RPC: Registered tcp NFSv4.1 backchannel transport module.
343 | [ 0.247457] PCI: CLS 0 bytes, default 64
344 | [ 0.250194] Unpacking initramfs...
345 | [ 0.252089] hw perfevents: enabled with armv8_pmuv3 PMU driver, 7 counters available
346 | [ 0.252719] kvm [1]: HYP mode not available
347 | [ 0.255075] Initialise system trusted keyrings
348 | [ 0.256216] workingset: timestamp_bits=42 max_order=15 bucket_order=0
349 | [ 0.264587] Freeing initrd memory: 4048K
350 | [ 0.264813] squashfs: version 4.0 (2009/01/31) Phillip Lougher
351 | [ 0.266781] NFS: Registering the id_resolver key type
352 | [ 0.267102] Key type id_resolver registered
353 | [ 0.267177] Key type id_legacy registered
354 | [ 0.267604] nfs4filelayout_init: NFSv4 File Layout Driver Registering...
355 | [ 0.267759] nfs4flexfilelayout_init: NFSv4 Flexfile Layout Driver Registering...
356 | [ 0.268362] 9p: Installing v9fs 9p2000 file system support
357 | [ 0.296720] Key type asymmetric registered
358 | [ 0.296866] Asymmetric key parser 'x509' registered
359 | [ 0.297224] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 245)
360 | [ 0.297383] io scheduler mq-deadline registered
361 | [ 0.297517] io scheduler kyber registered
362 | [ 0.313985] pl061_gpio 9030000.pl061: PL061 GPIO chip registered
363 | [ 0.335794] pci-host-generic 4010000000.pcie: host bridge /pcie@10000000 ranges:
364 | [ 0.336496] pci-host-generic 4010000000.pcie: IO 0x003eff0000..0x003effffff -> 0x0000000000
365 | [ 0.336995] pci-host-generic 4010000000.pcie: MEM 0x0010000000..0x003efeffff -> 0x0010000000
366 | [ 0.337139] pci-host-generic 4010000000.pcie: MEM 0x8000000000..0xffffffffff -> 0x8000000000
367 | [ 0.337729] pci-host-generic 4010000000.pcie: Memory resource size exceeds max for 32 bits
368 | [ 0.338221] pci-host-generic 4010000000.pcie: ECAM at [mem 0x4010000000-0x401fffffff] for [bus 00-ff]
369 | [ 0.339179] pci-host-generic 4010000000.pcie: PCI host bridge to bus 0000:00
370 | [ 0.339454] pci_bus 0000:00: root bus resource [bus 00-ff]
371 | [ 0.339581] pci_bus 0000:00: root bus resource [io 0x0000-0xffff]
372 | [ 0.339685] pci_bus 0000:00: root bus resource [mem 0x10000000-0x3efeffff]
373 | [ 0.339829] pci_bus 0000:00: root bus resource [mem 0x8000000000-0xffffffffff]
374 | [ 0.341081] pci 0000:00:00.0: [1b36:0008] type 00 class 0x060000
375 | [ 0.343668] pci 0000:00:01.0: [1af4:1000] type 00 class 0x020000
376 | [ 0.343989] pci 0000:00:01.0: reg 0x10: [io 0x0000-0x001f]
377 | [ 0.344113] pci 0000:00:01.0: reg 0x14: [mem 0x00000000-0x00000fff]
378 | [ 0.344331] pci 0000:00:01.0: reg 0x20: [mem 0x00000000-0x00003fff 64bit pref]
379 | [ 0.344512] pci 0000:00:01.0: reg 0x30: [mem 0x00000000-0x0007ffff pref]
380 | [ 0.346415] pci 0000:00:01.0: BAR 6: assigned [mem 0x10000000-0x1007ffff pref]
381 | [ 0.346725] pci 0000:00:01.0: BAR 4: assigned [mem 0x8000000000-0x8000003fff 64bit pref]
382 | [ 0.346953] pci 0000:00:01.0: BAR 1: assigned [mem 0x10080000-0x10080fff]
383 | [ 0.347074] pci 0000:00:01.0: BAR 0: assigned [io 0x1000-0x101f]
384 | [ 0.351329] EINJ: ACPI disabled.
385 | [ 0.374286] virtio-pci 0000:00:01.0: enabling device (0000 -> 0003)
386 | [ 0.385972] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
387 | [ 0.390186] SuperH (H)SCI(F) driver initialized
388 | [ 0.391020] msm_serial: driver initialized
389 | [ 0.393221] cacheinfo: Unable to detect cache hierarchy for CPU 0
390 | [ 0.416646] loop: module loaded
391 | [ 0.418396] megasas: 07.719.03.00-rc1
392 | [ 0.422165] physmap-flash 0.flash: physmap platform flash device: [mem 0x00000000-0x03ffffff]
393 | [ 0.423432] 0.flash: Found 2 x16 devices at 0x0 in 32-bit bank. Manufacturer ID 0x000000 Chip ID 0x000000
394 | [ 0.423866] Intel/Sharp Extended Query Table at 0x0031
395 | [ 0.424794] Using buffer write method
396 | [ 0.425221] physmap-flash 0.flash: physmap platform flash device: [mem 0x04000000-0x07ffffff]
397 | [ 0.426214] 0.flash: Found 2 x16 devices at 0x0 in 32-bit bank. Manufacturer ID 0x000000 Chip ID 0x000000
398 | [ 0.426376] Intel/Sharp Extended Query Table at 0x0031
399 | [ 0.426803] Using buffer write method
400 | [ 0.426954] Concatenating MTD devices:
401 | [ 0.427018] (0): "0.flash"
402 | [ 0.427070] (1): "0.flash"
403 | [ 0.427111] into device "0.flash"
404 | [ 0.447101] tun: Universal TUN/TAP device driver, 1.6
405 | [ 0.455641] thunder_xcv, ver 1.0
406 | [ 0.455774] thunder_bgx, ver 1.0
407 | [ 0.455875] nicpf, ver 1.0
408 | [ 0.457608] hns3: Hisilicon Ethernet Network Driver for Hip08 Family - version
409 | [ 0.457692] hns3: Copyright (c) 2017 Huawei Corporation.
410 | [ 0.458149] hclge is initializing
411 | [ 0.458287] e1000: Intel(R) PRO/1000 Network Driver
412 | [ 0.458358] e1000: Copyright (c) 1999-2006 Intel Corporation.
413 | [ 0.458499] e1000e: Intel(R) PRO/1000 Network Driver
414 | [ 0.458558] e1000e: Copyright(c) 1999 - 2015 Intel Corporation.
415 | [ 0.458704] igb: Intel(R) Gigabit Ethernet Network Driver
416 | [ 0.458766] igb: Copyright (c) 2007-2014 Intel Corporation.
417 | [ 0.459007] igbvf: Intel(R) Gigabit Virtual Function Network Driver
418 | [ 0.459083] igbvf: Copyright (c) 2009 - 2012 Intel Corporation.
419 | [ 0.459626] sky2: driver version 1.30
420 | [ 0.461268] VFIO - User Level meta-driver version: 0.3
421 | [ 0.466328] usbcore: registered new interface driver usb-storage
422 | [ 0.471491] rtc-pl031 9010000.pl031: registered as rtc0
423 | [ 0.472031] rtc-pl031 9010000.pl031: setting system clock to 2023-11-07T06:02:08 UTC (1699336928)
424 | [ 0.473346] i2c_dev: i2c /dev entries driver
425 | [ 0.482225] sdhci: Secure Digital Host Controller Interface driver
426 | [ 0.482316] sdhci: Copyright(c) Pierre Ossman
427 | [ 0.483338] Synopsys Designware Multimedia Card Interface Driver
428 | [ 0.484559] sdhci-pltfm: SDHCI platform and OF driver helper
429 | [ 0.487468] ledtrig-cpu: registered to indicate activity on CPUs
430 | [ 0.490437] usbcore: registered new interface driver usbhid
431 | [ 0.490514] usbhid: USB HID core driver
432 | [ 0.499497] NET: Registered PF_PACKET protocol family
433 | [ 0.500241] 9pnet: Installing 9P2000 support
434 | [ 0.500450] Key type dns_resolver registered
435 | [ 0.501267] registered taskstats version 1
436 | [ 0.501467] Loading compiled-in X.509 certificates
437 | [ 0.520843] input: gpio-keys as /devices/platform/gpio-keys/input/input0
438 | [ 0.543124] ALSA device list:
439 | [ 0.543312] No soundcards found.
440 | [ 0.546543] uart-pl011 9000000.pl011: no DMA platform data
441 | [ 0.612888] Freeing unused kernel memory: 1920K
442 | [ 0.614508] Run /init as init process
443 | [ 0.666661] rust_minimal: Rust minimal sample (init)
444 | [ 0.666979] rust_minimal: Am I built-in? false
445 | [ 0.675964] rust_minimal: My numbers are [72, 108, 200]
446 | [ 0.676445] rust_minimal: Rust minimal sample (exit)
447 | [ 0.720870] rust_print: Rust printing macros sample (init)
448 | [ 0.721035] rust_print: Emergency message (level 0) without args
449 | [ 0.721234] rust_print: Alert message (level 1) without args
450 | [ 0.721483] rust_print: Critical message (level 2) without args
451 | [ 0.721705] rust_print: Error message (level 3) without args
452 | [ 0.721774] rust_print: Warning message (level 4) without args
453 | [ 0.721836] rust_print: Notice message (level 5) without args
454 | [ 0.721913] rust_print: Info message (level 6) without args
455 | [ 0.721982] rust_print: A line that is continued without args
456 | [ 0.722114] rust_print: Emergency message (level 0) with args
457 | [ 0.722220] rust_print: Alert message (level 1) with args
458 | [ 0.722285] rust_print: Critical message (level 2) with args
459 | [ 0.722362] rust_print: Error message (level 3) with args
460 | [ 0.722426] rust_print: Warning message (level 4) with args
461 | [ 0.722496] rust_print: Notice message (level 5) with args
462 | [ 0.722589] rust_print: Info message (level 6) with args
463 | [ 0.722715] rust_print: A line that is continued with args
464 | [ 0.727927] rust_print: Rust printing macros sample (exit)
465 | [ 0.749354] rust_module_parameters: Rust module parameters sample (init)
466 | [ 0.749642] rust_module_parameters: Parameters:
467 | [ 0.750058] rust_module_parameters: my_bool: true
468 | [ 0.750170] rust_module_parameters: my_i32: 42
469 | [ 0.750430] rust_module_parameters: my_str: default str val
470 | [ 0.750545] rust_module_parameters: my_usize: 42
471 | [ 0.750700] rust_module_parameters: my_array: [0, 1]
472 | [ 0.755527] rust_module_parameters: Rust module parameters sample (exit)
473 | [ 0.780268] rust_sync: Rust synchronisation primitives sample (init)
474 | [ 0.780446] rust_sync: Value: 10
475 | [ 0.780799] rust_sync: Value: 10
476 | [ 0.785505] rust_sync: Rust synchronisation primitives sample (exit)
477 | [ 0.813859] rust_chrdev: Rust character device sample (init)
478 | [ 0.819166] rust_chrdev: Rust character device sample (exit)
479 | [ 0.841260] rust_miscdev: Rust miscellaneous device sample (init)
480 | [ 0.847056] rust_miscdev: Rust miscellaneous device sample (exit)
481 | [ 0.872530] rust_stack_probing: Rust stack probing sample (init)
482 | [ 0.872732] rust_stack_probing: Large array has length: 514
483 | [ 0.877872] rust_stack_probing: Rust stack probing sample (exit)
484 | [ 0.906001] rust_semaphore: Rust semaphore sample (init)
485 | [ 0.911501] rust_semaphore: Rust semaphore sample (exit)
486 | [ 0.936540] rust_semaphore_c: Rust semaphore sample (in C, for comparison) (init)
487 | [ 0.941912] rust_semaphore_c: Rust semaphore sample (in C, for comparison) (exit)
488 | [ 0.964929] rust_selftests: Rust self tests (init)
489 | [ 0.965048] rust_selftests: test_example passed!
490 | [ 0.965115] rust_selftests: 1 tests run, 1 passed, 0 failed, 0 hit errors
491 | [ 0.965453] rust_selftests: All tests passed. Congratulations!
492 | [ 0.971363] rust_selftests: Rust self tests (exit)
493 | [ 0.997060] rust_module_parameters_loadable_default: Rust module parameters sample (init)
494 | [ 0.997242] rust_module_parameters_loadable_default: Parameters:
495 | [ 0.997390] rust_module_parameters_loadable_default: my_bool: true
496 | [ 0.997531] rust_module_parameters_loadable_default: my_i32: 42
497 | [ 0.997992] rust_module_parameters_loadable_default: my_str: default str val
498 | [ 0.998271] rust_module_parameters_loadable_default: my_usize: 42
499 | [ 0.998512] rust_module_parameters_loadable_default: my_array: [0, 1]
500 | [ 1.007882] rust_module_parameters_loadable_custom: Rust module parameters sample (init)
501 | [ 1.008054] rust_module_parameters_loadable_custom: Parameters:
502 | [ 1.008180] rust_module_parameters_loadable_custom: my_bool: false
503 | [ 1.008445] rust_module_parameters_loadable_custom: my_i32: 345543
504 | [ 1.008939] rust_module_parameters_loadable_custom: my_str: 🦀mod
505 | [ 1.009066] rust_module_parameters_loadable_custom: my_usize: 84
506 | [ 1.009352] rust_module_parameters_loadable_custom: my_array: [1, 2, 3]
507 | [ 1.016364] rust_module_parameters_loadable_default: Rust module parameters sample (exit)
508 | [ 1.035957] rust_module_parameters_loadable_custom: Rust module parameters sample (exit)
509 | [ 1.068096] Flash device refused suspend due to active operation (state 20)
510 | [ 1.068362] Flash device refused suspend due to active operation (state 20)
511 | [ 1.068791] reboot: Restarting system
512 | ```
513 |
514 | # Exercise 2
515 |
516 | ## 练习2: 自定义编写Rust内核驱动模块
517 |
518 | We will only do in-tree compilation. In this part, we need to build a minimal kernel module
519 |
520 | * 编写 `rust_helloworld` 模块
521 |
522 | ```shell
523 | ## 进入练习1所在源码目录
524 | cd $HOME/linux
525 |
526 | ## 添加 RustHelloWorld 模块源码
527 | cat < samples/rust/rust_helloworld.rs
528 | // SPDX-License-Identifier: GPL-2.0
529 | //! Rust minimal sample.
530 |
531 | use kernel::prelude::*;
532 |
533 | module! {
534 | type: RustHelloWorld,
535 | name: "rust_helloworld",
536 | author: "whocare",
537 | description: "hello world module in rust",
538 | license: "GPL",
539 | }
540 |
541 | struct RustHelloWorld {}
542 |
543 | impl kernel::Module for RustHelloWorld {
544 | fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result {
545 | pr_info!("Hello World from Rust module");
546 | Ok(RustHelloWorld {})
547 | }
548 | }
549 | EOF
550 | ```
551 |
552 | * 添加编译相关配置
553 |
554 | ```shell
555 | ## 在 samples/rust/Makefile 文件添加以下内容:
556 | obj-$(CONFIG_SAMPLE_RUST_HELLOWORLD) += rust_helloworld.o
557 |
558 | ## 在 samples/rust/Kconfig 文件添加以下内容:
559 | config SAMPLE_RUST_HELLOWORLD
560 | tristate "Print Hello World in Rust"
561 | help
562 | This option builds the Rust HelloWorld module sample.
563 |
564 | To compile this as a module, choose M here:
565 | the module will be called rust_helloworld.
566 |
567 | If unsure, say N.
568 |
569 | ## 在练习1基础上,添加 rust_helloworld 模块配置
570 | make ARCH=arm64 LLVM=1 O=build menuconfig
571 | ```
572 |
573 | 
574 |
575 | * 编译 `rust_helloworld` 模块
576 |
577 | ```shell
578 | ## 将 rust_helloworld 模块编译,编译成功后,会生成 build/samples/rust/rust_helloworld.ko 文件
579 | cd build && time make ARCH=arm64 LLVM=1 -j8
580 | ```
581 |
582 | * 修改 `qemu-init.sh` 脚本,增加 `rust_helloworld` 模块
583 |
584 | ```shell
585 | busybox insmod rust_helloworld.ko
586 | busybox rmmod rust_helloworld.ko
587 | ```
588 |
589 | * 修改镜像描述文件 `qemu-initramfs.desc`,增加 `rust_helloworld` 模块
590 |
591 | ```shell
592 | file /rust_helloworld.ko samples/rust/rust_helloworld.ko 0755 0 0
593 | ```
594 |
595 | * 启动 `qemu` 验证
596 |
597 | ```shell
598 | qemu-system-aarch64 \
599 | -kernel build/arch/arm64/boot/Image.gz \
600 | -initrd qemu-initramfs.img \
601 | -M virt \
602 | -cpu cortex-a72 \
603 | -smp 2 \
604 | -nographic \
605 | -vga none \
606 | -no-reboot \
607 | -append ' \
608 | rust_module_parameters_builtin_custom.my_bool=n \
609 | rust_module_parameters_builtin_custom.my_i32=345543 \
610 | rust_module_parameters_builtin_custom.my_str=🦀mod \
611 | rust_module_parameters_builtin_custom.my_usize=84 \
612 | rust_module_parameters_builtin_custom.my_array=1,2,3 \
613 | ' \
614 | | sed 's:\r$::'
615 | ```
616 |
617 | * 验证结果显示如下面红色框,则表示`rust_helloworld` 模块加载成功。
618 |
619 | 
620 |
621 | * 代码已上传至:https://github.com/lispking/rust-for-linux-e1000/commit/fe8497d45e5cf719a4c1816c4183c80a55816899
622 |
623 | ## 基于Qemu模拟器上的e1000网卡驱动框架,填充驱动初始化函数
624 |
625 | 1. 由于在练习1里面是用的.github/workflow里的脚本生成的镜像,那个镜像的问题是不会停留在内核里,所以需要重新做一个
626 |
627 | ```shell
628 | cd busybox-1.36.1
629 |
630 | # 上一次练习没有执行这一步
631 | make install
632 | ```
633 |
634 | 2. 由于实验用的是 `fujita/linux` 源码,需要将自带的 `e1000` 驱动先去掉,再重新编译内核
635 |
636 | 3. 启动脚本 `qemu-aarch64-test.sh` 改成下面:
637 |
638 | ```shell
639 | #!/bin/bash
640 |
641 | BUSYBOX=../busybox-1.36.1
642 | INITRD=${PWD}/initramfs.cpio.gz
643 | BUSYBOX_INSTALL_DIR=$BUSYBOX/_install
644 | MODULE_DIR=/lib/modules
645 |
646 | cat < $BUSYBOX_INSTALL_DIR/init
647 | #!/bin/busybox sh
648 |
649 | /bin/busybox mkdir -p /proc && /bin/busybox mount -t proc none /proc
650 |
651 | /bin/busybox echo -e "\033[33m[$(date)] Hello, Welcome to Rust for Linux! \033[0m"
652 |
653 | /bin/busybox ls $MODULE_DIR
654 | /bin/busybox insmod $MODULE_DIR/e1000_for_linux.ko
655 |
656 | /bin/busybox ip addr add 127.0.0.1/32 dev lo
657 | /bin/busybox ip link set lo up
658 |
659 | /bin/busybox ip addr add 192.168.100.223/24 dev eth0
660 | /bin/busybox ip link set eth0 up
661 |
662 | export 'PS1=(kernel) >'
663 | /bin/busybox sh
664 | EOF
665 |
666 | # 参考这里: https://www.jianshu.com/p/9b68e9ea5849
667 | # Set host-only network
668 | if [ "$1" == "init-host-only" ]; then
669 | sudo ip link add br0 type bridge
670 | sudo ip addr add 192.168.100.50/24 brd 192.168.100.255 dev br0
671 | sudo ip tuntap add mode tap user $(whoami)
672 | ip tuntap show
673 | sudo ip link set tap0 master br0
674 | sudo ip link set dev br0 up
675 | sudo ip link set dev tap0 up
676 | fi
677 |
678 | chmod +x $BUSYBOX_INSTALL_DIR/init
679 |
680 | mkdir -p $BUSYBOX_INSTALL_DIR/$MODULE_DIR/
681 |
682 | ## 网卡驱动 ko 文件
683 | cp ../rust-e1000-driver/src/e1000_for_linux.ko $BUSYBOX_INSTALL_DIR/$MODULE_DIR/
684 |
685 | cd $BUSYBOX_INSTALL_DIR && find . -print0 | cpio --null -ov --format=newc | gzip -9 > ${INITRD} && cd -
686 |
687 | qemu-system-aarch64 \
688 | -kernel ./build/arch/arm64/boot/Image.gz \
689 | -initrd ${INITRD} \
690 | -M virt \
691 | -cpu cortex-a72 \
692 | -smp 2 \
693 | -m 128M \
694 | -nographic \
695 | -netdev tap,ifname=tap0,id=tap0,script=no,downscript=no -device e1000,netdev=tap0 \
696 | -append 'init=/init console=ttyAMA0'
697 | ```
698 |
699 | 4. 左边窗口执行 `./qemu-aarch64-test.sh`,网卡驱动和 `ip` 地址在 `init` 脚本里已实现自动加载,待 `qemu` 启动后,在右边窗口执行 `ping` 命令,运行成功,结果将如下所示:
700 |
701 | 
702 |
--------------------------------------------------------------------------------
/优秀文档参考/Rust实现内存块ramdisk设备驱动-江昊.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust实现内存块ramdisk设备驱动-江昊
3 | Date: 2023-11
4 | Tags:
5 | - Author: 江昊
6 | - Reports Repo: https://github.com/xxkeming/rust/blob/main/ramdisk.md
7 | ---
8 |
9 | ## 源码下载
10 | ```
11 | git clone https://github.com/Rust-for-Linux/linux -b rust-dev --depth 1
12 | ```
13 |
14 | ## 项目目标
15 | ```
16 | rust实现基于内存的块设备模块,有部分内存读写的宏太复杂,还是c实现
17 | ```
18 |
19 | ## blk-mq的binding添加,部分结构的封装
20 | ### rust实现基于内存的块设备模块
21 | ### bindings_helper.h 添加需要引用的头文件
22 | ```
23 | #include
24 | #include
25 | ```
26 | ### rust/kernel目录添加blkmq.rs,并重新编译内核
27 | ```
28 | use crate::bindings;
29 | use crate::sync::LockClassKey;
30 | use alloc::boxed::Box;
31 |
32 | /// gendisk
33 | pub struct GenDisk(*mut bindings::gendisk);
34 |
35 | unsafe impl Sync for GenDisk {}
36 | unsafe impl Send for GenDisk {}
37 |
38 | impl core::ops::Deref for GenDisk {
39 | type Target = bindings::gendisk;
40 |
41 | fn deref(&self) -> &Self::Target {
42 | unsafe { &*self.0 }
43 | }
44 | }
45 |
46 | impl core::ops::DerefMut for GenDisk {
47 |
48 | fn deref_mut(&mut self) -> &mut Self::Target {
49 | unsafe { &mut *self.0 }
50 | }
51 | }
52 |
53 | impl GenDisk {
54 |
55 | pub fn set_name(&mut self, name: &[u8]) {
56 |
57 | unsafe {
58 | core::ptr::copy_nonoverlapping(name.as_ptr(), self.disk_name.as_mut_ptr() as *mut u8, name.len());
59 | }
60 | }
61 |
62 | pub fn set_capacity(&mut self, sector: u64) {
63 |
64 | unsafe {
65 | bindings::set_capacity(&mut **self, sector)
66 | }
67 | }
68 |
69 | pub fn add(&mut self) -> isize {
70 |
71 | unsafe {
72 | bindings::device_add_disk(core::ptr::null_mut(), &mut **self, core::ptr::null_mut()) as isize
73 | }
74 | }
75 | }
76 |
77 | impl Drop for GenDisk {
78 |
79 | fn drop(&mut self) {
80 | unsafe {
81 | bindings::put_disk(&mut **self)
82 | }
83 | }
84 | }
85 |
86 | /// blk mq tag set
87 | pub struct Tagset(Box);
88 |
89 | unsafe impl Sync for Tagset {}
90 | unsafe impl Send for Tagset {}
91 |
92 | impl Default for Tagset {
93 |
94 | fn default() -> Self {
95 | Self ( Box::try_new(bindings::blk_mq_tag_set::default()).unwrap() )
96 | }
97 | }
98 |
99 | impl Tagset {
100 |
101 | pub fn alloc(&mut self) -> isize {
102 |
103 | unsafe { bindings::blk_mq_alloc_tag_set(&mut **self) as isize }
104 | }
105 |
106 | pub fn alloc_disk(&mut self) -> Option {
107 |
108 | static KEY: LockClassKey = LockClassKey::new();
109 |
110 | unsafe
111 | {
112 | let disk = crate::error::from_err_ptr::(bindings::__blk_mq_alloc_disk(&mut **self, core::ptr::null_mut(), KEY.as_ptr()));
113 | if let Ok(disk) = disk {
114 | Some(GenDisk(disk))
115 | } else {
116 | None
117 | }
118 | }
119 | }
120 | }
121 |
122 | impl Drop for Tagset {
123 |
124 | fn drop(&mut self) {
125 |
126 | unsafe {
127 | /*if self.driver_data != core::ptr::null() {
128 | bindings::vfree(self.driver_data);
129 | }*/
130 | bindings::blk_mq_free_tag_set(&mut **self)
131 | }
132 | }
133 | }
134 |
135 | impl core::ops::Deref for Tagset {
136 | type Target = bindings::blk_mq_tag_set;
137 |
138 | fn deref(&self) -> &Self::Target {
139 | &self.0
140 | }
141 | }
142 |
143 | impl core::ops::DerefMut for Tagset {
144 |
145 | fn deref_mut(&mut self) -> &mut Self::Target {
146 | &mut self.0
147 | }
148 | }
149 |
150 | /// blk mq ops
151 | pub struct Ops(Box);
152 |
153 | unsafe impl Sync for Ops {}
154 | unsafe impl Send for Ops {}
155 |
156 | impl Ops {
157 |
158 | pub fn new() -> Self {
159 | Self ( Box::try_new(bindings::blk_mq_ops::default()).unwrap() )
160 | }
161 | }
162 |
163 | impl core::ops::Deref for Ops {
164 | type Target = bindings::blk_mq_ops;
165 |
166 | fn deref(&self) -> &Self::Target {
167 | &self.0
168 | }
169 | }
170 |
171 | impl core::ops::DerefMut for Ops {
172 |
173 | fn deref_mut(&mut self) -> &mut Self::Target {
174 | &mut self.0
175 | }
176 | }
177 |
178 |
179 | /// blk mq ops
180 | pub struct BdOps(Box);
181 |
182 | unsafe impl Sync for BdOps {}
183 | unsafe impl Send for BdOps {}
184 |
185 | impl BdOps {
186 |
187 | pub fn new(module: &mut bindings::module ) -> Self {
188 |
189 | let mut ops = Box::try_new(bindings::block_device_operations::default()).unwrap();
190 | ops.owner = module;
191 | Self ( ops )
192 | }
193 | }
194 |
195 | impl core::ops::Deref for BdOps {
196 | type Target = bindings::block_device_operations;
197 |
198 | fn deref(&self) -> &Self::Target {
199 | &self.0
200 | }
201 | }
202 |
203 | impl core::ops::DerefMut for BdOps {
204 |
205 | fn deref_mut(&mut self) -> &mut Self::Target {
206 | &mut self.0
207 | }
208 | }
209 | ```
210 |
211 | ### 以动态的方式构造ramdisk模块
212 | ### Makefile文件
213 | ```
214 | ramdisk-objs := vlk_queue.o rustblk.o
215 | obj-m := ramdisk.o
216 | #obj-m := blk.o
217 |
218 | PWD := $(shell pwd)
219 | ARCH = x86_64
220 | #KDIR = /lib/modules/$(shell uname -r)/build
221 | KDIR = /home/parallels/linux/build
222 |
223 | default:
224 | $(MAKE) ARCH=$(ARCH) LLVM=1 -C $(KDIR) M=$(PWD)/ modules
225 | clean:
226 | $(MAKE) ARCH=$(ARCH) LLVM=1 -C $(KDIR) M=$(PWD)/ clean
227 | ```
228 |
229 | ### vlk_queue.c 部分函数太复杂,还是c实现方便
230 | ```
231 | #include
232 | #include
233 | #include
234 |
235 | blk_status_t vblk_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd)
236 | {
237 | struct request *rq = bd->rq;
238 | struct bio_vec bvec;
239 | struct req_iterator iter;
240 | u64 offset;
241 | u32 len;
242 | u8 *buf;
243 |
244 | //pr_info("vblk_queue_rq\n");
245 |
246 | blk_mq_start_request(rq);
247 |
248 | offset = blk_rq_pos(rq) * 512;
249 |
250 | rq_for_each_segment(bvec, rq, iter) {
251 | len = bvec.bv_len;
252 | buf = page_address(bvec.bv_page) + bvec.bv_offset;
253 | if (rq_data_dir(rq) == READ) {
254 | memcpy(buf, hctx->driver_data + offset, len);
255 | } else {
256 | memcpy(hctx->driver_data + offset, buf, len);
257 | }
258 | offset += len;
259 | }
260 |
261 | blk_mq_complete_request(rq);
262 |
263 | return BLK_STS_OK;
264 | }
265 | ```
266 |
267 | ### rustblk.rs
268 | ```
269 | #![allow(missing_docs)]
270 | #![allow(improper_ctypes)]
271 |
272 | use kernel::prelude::*;
273 | use kernel::blkmq;
274 | use kernel::bindings;
275 | use kernel::error;
276 |
277 | module! {
278 | type: RustBlock,
279 | name: "rust_block",
280 | author: "keming",
281 | description: "block module in rust",
282 | license: "GPL",
283 | }
284 |
285 | unsafe extern "C" fn vblk_open(_disk: *mut bindings::gendisk, _mode: bindings::blk_mode_t) -> i32
286 | {
287 | pr_info!("vblk_open\n");
288 | return 0;
289 | }
290 |
291 | unsafe extern "C" fn vblk_release(_disk: *mut bindings::gendisk)
292 | {
293 | pr_info!("vblk_release\n");
294 | }
295 |
296 |
297 | unsafe extern "C" fn vblk_init(hctx: *mut bindings::blk_mq_hw_ctx, data: *mut core::ffi::c_void, _arg3: u32) -> i32 {
298 |
299 | unsafe {
300 | pr_info!("vblk_init {:#x}\n", data as usize);
301 | (*hctx).driver_data = data;
302 | }
303 |
304 | 0
305 | }
306 |
307 | //*
308 | extern "C" {
309 | fn vblk_queue_rq(hctx: *mut bindings::blk_mq_hw_ctx, bd: *const bindings::blk_mq_queue_data) -> bindings::blk_status_t;
310 | }
311 | //*/
312 |
313 | /*
314 | unsafe extern "C" fn vblk_queue_rq(hctx: *mut bindings::blk_mq_hw_ctx, bd: *const bindings::blk_mq_queue_data) -> bindings::blk_status_t {
315 |
316 | unsafe {
317 | pr_info!("queue_rq data: {:#x}\n", (*hctx).driver_data as usize);
318 |
319 | let rq = (*bd).rq;
320 | let mut iter = bindings::req_iterator::default();
321 |
322 | bindings::blk_mq_start_request(rq);
323 | let offset = (*rq).__sector * 512;
324 |
325 | iter.bio = (*rq).bio;
326 |
327 | loop {
328 | if iter.bio == core::ptr::null_mut() {
329 | break;
330 | }
331 |
332 | iter.iter = (*iter.bio).bi_iter;
333 | loop {
334 | if iter.iter.bi_size < 1 {
335 | break;
336 | }
337 |
338 | let mut bv = bindings::bio_vec::default();
339 | bv.bv_page =
340 | iter.iter.bio.bi_io_vec
341 | iter.iter
342 |
343 | #define bvec_iter_bvec(bvec, iter) \
344 | ((struct bio_vec) { \
345 | .bv_page = bvec_iter_page((bvec), (iter)), \
346 | .bv_len = bvec_iter_len((bvec), (iter)), \
347 | .bv_offset = bvec_iter_offset((bvec), (iter)), \
348 | })
349 |
350 | }
351 |
352 | iter.bio = (*iter.bio).bi_next;
353 | }
354 |
355 | bindings::blk_mq_complete_request(rq);
356 | }
357 | bindings::BLK_STS_OK as bindings::blk_status_t
358 | }
359 | */
360 |
361 | unsafe extern "C" fn vblk_complete(rq: *mut bindings::request)
362 | {
363 | unsafe {
364 | //pr_info!("complete {:#x} {:#x}", rq as usize, core::mem::size_of::());
365 | //pr_info!("complete {:#x}", rq.add(1) as usize);
366 |
367 | let data = *(rq.add(1) as *mut usize).add(1);
368 | //pr_info!("complete kfree: {:#x}", data);
369 |
370 | if data != 0 {
371 | bindings::kfree(data as *const _);
372 | }
373 |
374 | bindings::blk_mq_end_request(rq, bindings::BLK_STS_OK as u8)
375 | }
376 | }
377 |
378 | struct RustBlock {
379 | ops: blkmq::Ops,
380 | bdops: blkmq::BdOps,
381 | tagset: blkmq::Tagset,
382 | disk: blkmq::GenDisk
383 | }
384 |
385 | impl kernel::Module for RustBlock {
386 |
387 | fn init(_module: &'static ThisModule) -> Result {
388 |
389 | let mut ops = blkmq::Ops::new();
390 | ops.init_hctx = Some(vblk_init);
391 | ops.queue_rq = Some(vblk_queue_rq);
392 | ops.complete = Some(vblk_complete);
393 |
394 | let mut tagset = blkmq::Tagset::default();
395 | tagset.ops = &*ops;
396 | tagset.nr_hw_queues = 4;
397 | tagset.queue_depth = 64;
398 | tagset.numa_node = bindings::NUMA_NO_NODE;
399 | tagset.cmd_size = 0;
400 | tagset.flags = bindings::BLK_MQ_F_SHOULD_MERGE;
401 | tagset.driver_data = unsafe { bindings::vzalloc(1024 * 1024 * 32) };
402 |
403 | if tagset.alloc() != 0 {
404 | unsafe { bindings::vfree(tagset.driver_data) };
405 |
406 | pr_err!("Failed to allocate tag set\n");
407 | return Err(error::code::EPERM);
408 | }
409 |
410 | let mut disk = match tagset.alloc_disk() {
411 | Some(r) => r,
412 | _ => {
413 | unsafe { bindings::vfree(tagset.driver_data) };
414 |
415 | pr_err!("Failed to add disk\n");
416 | return Err(error::code::EPERM);
417 | }
418 | };
419 | let mut bdops = unsafe { blkmq::BdOps::new(&mut bindings::__this_module) };
420 | bdops.open = Some(vblk_open);
421 | bdops.release = Some(vblk_release);
422 |
423 | disk.major = 0;
424 | disk.first_minor = 0;
425 | disk.fops = &*bdops;
426 | disk.set_name(b"vblk");
427 | disk.set_capacity((1024 * 1024 * 32) / 512);
428 |
429 | if disk.add() != 0 {
430 |
431 | unsafe { bindings::vfree(tagset.driver_data) };
432 |
433 | pr_err!("Failed to add disk\n");
434 | return Err(error::code::EPERM);
435 | }
436 |
437 | pr_info!("Rust module init {:p}\n", &*ops);
438 |
439 | Ok(RustBlock {ops, bdops, tagset, disk})
440 | }
441 | }
442 |
443 | impl Drop for RustBlock {
444 |
445 | fn drop(&mut self) {
446 |
447 | unsafe { bindings::vfree(self.tagset.driver_data) };
448 |
449 | pr_info!("Rust module drop {} {:#x} {:p} {:p} {:p}\n", self.tagset.nr_maps, self.tagset.ops as usize, &*self.ops, &*self.bdops, &self.disk);
450 | }
451 | }
452 | ```
453 |
454 | ### 编译,打包到busybox,加载效果结果
455 |
456 |
457 |
458 | ## 练习 1,2 作业
459 |
460 | ### 源码下载
461 | ```
462 | git clone https://github.com/Rust-for-Linux/linux -b rust-dev --depth 1
463 | git clone https://github.com/fujita/linux.git -b rust-e1000 --depth 1
464 | ```
465 |
466 | ### 环境搭建
467 | ```
468 | ## Install dependency packages
469 | sudo apt-get -y install \
470 | binutils build-essential libtool texinfo \
471 | gzip zip unzip patchutils curl git \
472 | make cmake ninja-build automake bison flex gperf \
473 | grep sed gawk bc \
474 | zlib1g-dev libexpat1-dev libmpc-dev \
475 | libglib2.0-dev libfdt-dev libpixman-1-dev libelf-dev libssl-dev
476 |
477 | ## Install the LLVM
478 | sudo apt-get install clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang
479 |
480 | ## Add Rust environment
481 | cd linux
482 | rustup override set $(scripts/min-tool-version.sh rustc)
483 | rustup component add rust-src
484 | cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli
485 | make LLVM=1 rustavailable
486 | ```
487 |
488 | ### 编译时遇到的问题 bindgen版本过高,修改了部分增对bindgen参数的Makefile配置
489 |
490 |
491 | ### 编译流程,配置开启rust,并把练习2的代码及配置加入
492 | ```
493 | make ARCH=arm64 LLVM=1 O=build defconfig
494 |
495 | make ARCH=arm64 LLVM=1 O=build menuconfig
496 |
497 | cd build
498 | make ARCH=arm64 LLVM=1 -j4
499 | ```
500 |
501 | ### 启动方式
502 | ```
503 | qemu-system-aarch64 -machine 'virt' -cpu 'cortex-a57' -m 1G -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22 -kernel /home/parallels/linux/build/arch/arm64/boot/Image.gz -initrd initrd -nographic -append "root=LABEL=rootfs console=ttyAMA0"
504 |
505 | ```
506 |
507 | ### 运行结果
508 |
509 |
510 |
511 | ## 练习 3,4 作业
512 | ### 网络环境配置
513 | ```
514 | 主机配置:
515 | sudo ip link add br0 type bridge
516 | sudo ip addr add 192.168.100.50/24 brd 192.168.100.255 dev br0
517 | sudo ip tuntap add mode tap user $(whoami)
518 | ip tuntap show
519 | sudo ip link set tap0 master br0
520 | sudo ip link set dev br0 up
521 | sudo ip link set dev tap0 up
522 |
523 | 虚拟机配置:
524 | ifconfig lo 127.0.0.1 netmask 255.0.0.0 up
525 | ifconfig eth0 192.168.100.224 netmask 255.255.255.0 broadcast 192.168.100.255 up
526 |
527 | qemu启动方式:
528 | qemu-system-aarch64 -M virt -cpu cortex-a57 -smp 1 -m 256M -kernel build/arch/arm64/boot/Image.gz -initrd ../busybox-1.36.1/initramfs.cpio.gz -nographic -vga none -no-reboot -append "init=/init console=ttyAMA0" -netdev tap,ifname=tap0,id=tap0,script=no,downscript=no -device e1000,netdev=tap0
529 | ```
530 | ### 添加自定义函数
531 | ```
532 | 1.直接在rust/helpers.c添加自定义函数void rust_helper_test(void)
533 | 2.在rust/bindings/lib.rs对bindings_helper添加pub
534 | 3.调用方式:kernel::bindings::bindings_helper::test()
535 | ```
536 | ### e100网卡驱动作业仓库地址及运行结果
537 | ```
538 | https://github.com/xxkeming/e1000-driver
539 | ```
540 |
541 |
542 | ## 补充: 自定义基本模块
543 | ### test.rs
544 | ```
545 | use kernel::prelude::*;
546 | module! {
547 | type: RustTest,
548 | name: "rust_test",
549 | author: "keming",
550 | description: "test module in rust",
551 | license: "GPL",
552 | }
553 | struct RustTest {}
554 | impl kernel::Module for RustTest {
555 | fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result {
556 | pr_info!("test from Rust module");
557 | Ok(RustTest {})
558 | }
559 | }
560 | ```
561 | ### makefile
562 | ```
563 | obj-m := test.o
564 | PWD := $(shell pwd)
565 | ARCH ?= arm64
566 | KDIR ?= /lib/modules/$(shell uname -r)/build
567 | default:
568 | $(MAKE) ARCH=$(ARCH) LLVM=1 -C $(KDIR) M=$(PWD)/ modules
569 | clean:
570 | $(MAKE) ARCH=$(ARCH) LLVM=1 -C $(KDIR) M=$(PWD)/ clean
571 | ```
572 |
--------------------------------------------------------------------------------
/优秀文档参考/lcx-Rust-for-Linux-Virtio驱动模块开发.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust for Linux Virtio驱动模块开发-lcx
3 | Date: 2023-11
4 | Tags:
5 | - Author: lcx
6 | - Reports Repo: https://github.com/451846939/TimeMachine/blob/master/src/os/rust-for-linux/rust-for-linux%E2%80%94Virtio%E7%9A%84%E5%AE%9E%E7%8E%B0.md
7 | ---
8 |
9 | # 实验virtio
10 |
11 | 这个就是真的由于时间关系还有驱动不完善,各自有各自的实现,兼容这些问题导致浪费了大量的时间
12 |
13 | 还有要理解一下bus、device、device_driver之间的关系
14 |
15 | 如果实现virtio_blk或者别的,一定要去看一下c实现,还有linux里这3个源码文件的结构体定义,看了以后才能理解一些nvme驱动的封装,还有内存的分配在实验的rust e1000里分配内存使用了dma分配,内核里大部分是kmalloc
16 |
17 | 这里发现还有很多知识的欠缺和断链比如rust的内存分配是如何分配的,在rust-for-linux中都有答案,所以想要用rust写好驱动这些基础模块实现和linux中的差别以及如何封装的一定要了解
18 |
19 | 在写的过程中最需要操作的就是如何把c代码转换为rust的抽象,c的实现其实个人觉得已经很优秀,转换为rust就需要把c面向过程语言是如何实现类似trait功能的理解清楚,之后再根据自己的理解去抽象成rust代码
20 |
21 | 时间关系,最后一个实验实现的东西并不多。。。环境问题和理解比较花时间。。后续也会继续抽时间玩这个东西
22 |
23 | 总体来说,学到了很多,也玩到了rust-for-linux,并且体验到了驱动的编写,通过e1000也了解了网卡那块的处理,包括通过virtio的学习也理解了自己在使用虚拟机读取本机文件的时候是怎么操作的
24 |
25 | ## rust-for-linux—Virtio 的实现
26 |
27 | > https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.html
28 | >
29 | > https://www.openeuler.org/zh/blog/yorifang/virtio-spec-overview.html
30 | >
31 | > https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter9/2device-driver-2.html# 强烈推荐阅读这个
32 | >
33 | > https://zhuanlan.zhihu.com/p/642100699
34 | >
35 | > https://mp.weixin.qq.com/s/Stmj1weLXUg_EzVJfCIB3g
36 | >
37 | > https://mp.weixin.qq.com/s/6qo2xOUdqCEBOXhbKVNZSA
38 |
39 |
40 |
41 | 
42 |
43 |
44 |
45 | ### virtio设备基本组成要素
46 |
47 | `virtio`设备的基本组成要素如下:
48 |
49 | - 设备状态域(Device status field)
50 | - 特征位(Feature bits)
51 | - 通知(Notifications)
52 | - 设备配置空间(Device Configuration space)
53 | - 一个或多个虚拟队列(virtqueue)
54 |
55 |
56 |
57 | ### virtio设备呈现模式
58 |
59 | `virtio`设备支持三种设备呈现模式:
60 |
61 | - **Virtio Over MMIO**,虚拟设备直接挂载到系统总线上,我们实验中的虚拟计算机就是这种呈现模式;可参考linux内核中`virtio_mmio`
62 | - **Virtio Over PCI BUS**,遵循PCI规范,挂在到PCI总线上,作为virtio-pci设备呈现,在QEMU虚拟的x86计算机上采用的是这种模式;可参考linux内核中`virtio_pci`
63 | - **Virtio Over Channel I/O**:主要用在虚拟IBM s390计算机上,virtio-ccw使用这种基于channel I/O的机制。
64 |
65 |
66 |
67 | ### virtio高层架构
68 |
69 | 
70 |
71 |
72 |
73 | 在Linux源码中也可以看见在`linux/drivers/virtio` 对`virtio`的实现源码
74 |
75 | 
76 |
77 | 实现`virtio`的关键在于`virtioqueue`操作
78 |
79 | ### virtioqueue
80 |
81 | virtqueue由三部分组成:
82 |
83 | - 描述符表 Descriptor Table:描述符表是描述符为组成元素的数组,每个描述符描述了一个内存buffer 的address/length。而内存buffer中包含I/O请求的命令/数据(由virtio设备驱动填写),也可包含I/O完成的返回结果(由virtio设备填写)等。
84 | - 可用环 Available Ring:一种vring,记录了virtio设备驱动程序发出的I/O请求索引,即被virtio设备驱动程序更新的描述符索引的集合,需要virtio设备进行读取并完成相关I/O操作;
85 | - 已用环 Used Ring:另一种vring,记录了virtio设备发出的I/O完成索引,即被virtio设备更新的描述符索引的集合,需要vrtio设备驱动程序进行读取并对I/O操作结果进行进一步处理。
86 |
87 | 
88 |
89 | ## 2. 前置知识
90 |
91 | 要想用rust-for-linux实现virtio,我们首先可以参考一下linux是如何实现的
92 |
93 | 首先我们把目光看到`virtio.c` 看下我们linux的驱动入口
94 |
95 | ```c
96 | static int virtio_init(void)
97 | {
98 | if (bus_register(&virtio_bus) != 0)
99 | panic("virtio bus registration failed");
100 | return 0;
101 | }
102 |
103 | static void __exit virtio_exit(void)
104 | {
105 | bus_unregister(&virtio_bus);
106 | ida_destroy(&virtio_index_ida);
107 | }
108 | core_initcall(virtio_init);
109 | module_exit(virtio_exit);
110 | ```
111 |
112 | 这里进行了`bus_register`方法的调用,要看懂这个操作我们还要理解一下bus、device、device_driver之间的关系
113 |
114 | > 在 Linux 内核中,"bus"(总线)是一种抽象概念,它表示一组相互连接的设备和资源。总线提供了设备之间进行通信和协作的框架,使得内核能够有效地管理和控制这些设备。
115 | >
116 | > 总线的概念有助于将设备模型中的各个部分分隔开来,使得内核能够以一致的方式与各种不同类型的设备进行交互。在这个上下文中,"bus" 并不是物理上的电缆或连接,而是一个在软件层面上建立的组织结构。
117 | >
118 | > 在 Linux 内核中,总线可以是硬件总线(如 PCI 总线、USB 总线、I2C 总线等)或虚拟总线(如 sysfs)。总线的注册和管理是通过内核的设备模型来实现的。
119 | >
120 | > 总线的角色包括:
121 | >
122 | > 1. **设备的注册和发现:** 设备驱动可以将自己注册到适当的总线上,从而告诉内核它支持的设备。内核可以在启动时或运行时发现和注册连接到总线上的设备。
123 | > 2. **设备的资源分配:** 总线可以负责为连接到它上面的设备分配和管理资源,如中断、I/O 端口、内存地址等。
124 | > 3. **设备的通信:** 总线提供了设备之间通信的基础。不同设备可以通过总线进行数据传输、同步和协作。
125 | >
126 | > 总线是设备模型的一部分,通过它,内核能够有效地组织、注册和管理系统中的各种设备。总线的概念使得内核能够以一致的方式处理不同类型和架构的设备。
127 | >
128 | > https://docs.kernel.org/driver-api/driver-model/bus.html
129 | > https://mp.weixin.qq.com/s/CIp1ykYasQFKuubV8atE0w
130 | >
131 | > https://mp.weixin.qq.com/s/3pGZXwA9WPPMlWZltu5xMg
132 |
133 | ### bus_type (总线)
134 |
135 | Linux将设备和驱动都挂载在总线上,管理着设备和驱动的注册、匹配、注销等操作,而内核中的每一个bus总线是由`struct bus_type`结构体描述的`bus_type` 的定义如下
136 |
137 | ```c
138 | struct bus_type {
139 | const char *name; // 该总线的名称
140 | const char *dev_name; // 当设备的名称为空时,就会以bus->dev_name + device ID呈现
141 | struct device *dev_root; // 为该bus的默认父设备
142 | const struct attribute_group **bus_groups;
143 | const struct attribute_group **dev_groups;
144 | const struct attribute_group **drv_groups;
145 |
146 | int (*match)(struct device *dev, struct device_driver *drv);
147 | int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
148 | int (*probe)(struct device *dev);
149 | int (*remove)(struct device *dev);
150 | void (*shutdown)(struct device *dev);
151 |
152 | int (*online)(struct device *dev);
153 | int (*offline)(struct device *dev);
154 |
155 | int (*suspend)(struct device *dev, pm_message_t state);
156 | int (*resume)(struct device *dev);
157 |
158 | int (*num_vf)(struct device *dev);
159 |
160 | int (*dma_configure)(struct device *dev);
161 |
162 | const struct dev_pm_ops *pm;
163 |
164 | const struct iommu_ops *iommu_ops;
165 |
166 | struct subsys_private *p; // 私有数据
167 | struct lock_class_key lock_key;
168 |
169 | bool need_parent_lock;
170 | };
171 | ```
172 |
173 | - name: 总线名称,在sysfs中以目录的形式存在,比如i2c总线表现为“/sys/bus/i2c”。
174 | - dev_name:当设备的名称为空时,就会以bus->dev_name + device ID呈现
175 | - bus_groups、dev_groups、drv_groups分别表示bus、device、driver的默认属性attribute,在初始化时自动添加它们。
176 | - match回调函数:当任何属于该bus的device或device_driver添加时,Linux内核都会调用该接口进行匹配处理。
177 | - uevent回调函数:当该属于该bus的device发生添加、删除或其他动作时,bus的核心逻辑会调用该接口,以便bus driver可以修改环境变量。
178 | - probe和remove回调函数:当device和device_driver匹配成功时,将会调用总线的probe实现具体的driver初始化,而总线也有自己的初始化函数,一般地,先调用总线的probe函数,然后在总线probe函数中调用driver的probe函数。相应地,bus提供的remove函数是在一处挂载在设备上的driver时,先执行driver的remove函数,然后再调用ubs的remove函数。
179 | - shutdown、online、offline、suspend、resume、pm是电源管理相关。
180 | - p:bus的私有指针。
181 |
182 | 下图是PCI的总线图
183 |
184 |
185 |
186 | 
187 |
188 |
189 |
190 | ### device (设备)
191 |
192 | Linux内核中描述设备最基本的结构体是`struct device`
193 |
194 | ```c
195 | struct device {
196 | struct kobject kobj;
197 | struct device *parent;
198 |
199 | struct device_private *p;
200 |
201 | const char *init_name; /* initial name of the device */
202 | const struct device_type *type;
203 |
204 | struct bus_type *bus; /* type of bus device is on */
205 | struct device_driver *driver; /* which driver has allocated this
206 | device */
207 | void *platform_data; /* Platform specific data, device
208 | core doesn't touch it */
209 | void *driver_data; /* Driver data, set and get with
210 | dev_set_drvdata/dev_get_drvdata */
211 | #ifdef CONFIG_PROVE_LOCKING
212 | struct mutex lockdep_mutex;
213 | #endif
214 | struct mutex mutex; /* mutex to synchronize calls to
215 | * its driver.
216 | */
217 |
218 | struct dev_links_info links;
219 | struct dev_pm_info power;
220 | struct dev_pm_domain *pm_domain;
221 |
222 | #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
223 | struct irq_domain *msi_domain;
224 | #endif
225 | #ifdef CONFIG_PINCTRL
226 | struct dev_pin_info *pins;
227 | #endif
228 | #ifdef CONFIG_GENERIC_MSI_IRQ
229 | struct list_head msi_list;
230 | #endif
231 |
232 | const struct dma_map_ops *dma_ops;
233 | u64 *dma_mask; /* dma mask (if dma'able device) */
234 | u64 coherent_dma_mask;/* Like dma_mask, but for
235 | alloc_coherent mappings as
236 | not all hardware supports
237 | 64 bit addresses for consistent
238 | allocations such descriptors. */
239 | u64 bus_dma_mask; /* upstream dma_mask constraint */
240 | unsigned long dma_pfn_offset;
241 |
242 | struct device_dma_parameters *dma_parms;
243 |
244 | struct list_head dma_pools; /* dma pools (if dma'ble) */
245 |
246 | #ifdef CONFIG_DMA_DECLARE_COHERENT
247 | struct dma_coherent_mem *dma_mem; /* internal for coherent mem
248 | override */
249 | #endif
250 | #ifdef CONFIG_DMA_CMA
251 | struct cma *cma_area; /* contiguous memory area for dma
252 | allocations */
253 | #endif
254 | /* arch specific additions */
255 | struct dev_archdata archdata;
256 |
257 | struct device_node *of_node; /* associated device tree node */
258 | struct fwnode_handle *fwnode; /* firmware device node */
259 |
260 | #ifdef CONFIG_NUMA
261 | int numa_node; /* NUMA node this device is close to */
262 | #endif
263 | dev_t devt; /* dev_t, creates the sysfs "dev" */
264 | u32 id; /* device instance */
265 |
266 | spinlock_t devres_lock;
267 | struct list_head devres_head;
268 |
269 | struct class *class;
270 | const struct attribute_group **groups; /* optional groups */
271 |
272 | void (*release)(struct device *dev);
273 | struct iommu_group *iommu_group;
274 | struct iommu_fwspec *iommu_fwspec;
275 | struct iommu_param *iommu_param;
276 |
277 | bool offline_disabled:1;
278 | bool offline:1;
279 | bool of_node_reused:1;
280 | #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
281 | defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
282 | defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
283 | bool dma_coherent:1;
284 | #endif
285 | };
286 | ```
287 |
288 | 在Linux内核中,每个设备都表示为`struct device`的一个实例,该结构体包含了设备模型核心建模所需的信息。`struct device`很少单独使用,更多的是嵌入到一个更高层次的设备结构体中。
289 |
290 |
291 |
292 | - parent:本设备的父节点,大部分情况下,父节点是bus节点或主控制器,如果父节点为NULL,那就是一个(top-level)顶层设备,一般不是我们想要的。
293 | - p:用于保存设备驱动核心部分的私有数据
294 | - kobj:该数据结构对应的struct kobject
295 | - init_name:设备的名称
296 | - type:设备类型
297 | - bus:该设备挂在哪个总线上
298 | - driver:该设备对应的驱动
299 | - platform_data:设备的平台数据,例如:对于定制版的设备,典型的是嵌入式和基于SOC的硬件,linux经常使用platform_data指向该板的数据结构体,描述设备及其链接方式,这可能包含了可用的端口,芯片变量,GPIO等等,这种做法缩小了BSP,并最小化驱动中的#ifdefs。
300 | - driver_data:驱动程序特定数据的指针
301 | - power,pm_domain:和电源管理相关
302 | - pins:设备的引脚管理相关
303 | - dma_ops,dma_mask,bus_dma_mask,dma_pfn_offset,dma_parms,dma_pools,dma_mem:DMA操作相关
304 | - devt:创建sysfs的dev,也叫设备号,一般由Major和Minor两部分组成。
305 |
306 |
307 |
308 | ### device_driver (驱动)
309 |
310 | Linux内核中描述设备驱动最基本的结构体是`struct device_driver`
311 |
312 | ```c
313 | struct device_driver {
314 | const char *name; // 驱动名称
315 | struct bus_type *bus; // 此驱动程序的设备所属的总线
316 |
317 | struct module *owner;
318 | const char *mod_name; /* used for built-in modules */
319 |
320 | bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
321 | enum probe_type probe_type; // probe类型 synchronous or asynchronous
322 |
323 | const struct of_device_id *of_match_table; // open firmware匹配表
324 | const struct acpi_device_id *acpi_match_table; // ACPI匹配表
325 |
326 | int (*probe) (struct device *dev);
327 | int (*remove) (struct device *dev);
328 | void (*shutdown) (struct device *dev);
329 | int (*suspend) (struct device *dev, pm_message_t state);
330 | int (*resume) (struct device *dev);
331 | const struct attribute_group **groups; // 由驱动内核自动创建的默认属性
332 | const struct attribute_group **dev_groups; // 设备实例绑定到驱动程序后,附加到设备的其他属性
333 |
334 | const struct dev_pm_ops *pm; // power management相关
335 | void (*coredump) (struct device *dev); // 用于产生uevent
336 |
337 | struct driver_private *p; // 私有数据
338 | };
339 | ```
340 |
341 |
342 |
343 | - name,该driver的名称。和device结构一样,该名称非常重要,后面会再详细说明。
344 | - bus,该driver所驱动设备的总线设备。为什么driver需要记录总线设备的指针呢?因为内核要保证在driver运行前,设备所依赖的总线能够正确初始化。
345 | - owner、mod_name,內核module相关的变量,暂不描述。
346 | - suppress_bind_attrs,是不在sysfs中启用bind和unbind attribute。在kernel中,bind/unbind是从用户空间手动的为driver绑定/解绑定指定的设备的机制。这种机制是在bus.c中完成的,后面会详细解释。
347 | - probe、remove,这两个接口函数用于实现driver逻辑的开始和结束。Driver是一段软件code,因此会有开始和结束两个代码逻辑,就像PC程序,会有一个main函数,main函数的开始就是开始,return的地方就是结束。而内核driver却有其特殊性:在设备模型的结构下,只有driver和device同时存在时,才需要开始执行driver的代码逻辑。这也是probe和remove两个接口名称的由来:检测到了设备和移除了设备(就是为热拔插起的!)。
348 | - shutdown、suspend、resume、pm,电源管理相关的内容。
349 | - groups,和struct device结构中的同名变量类似,driver也可以定义一些默认attribute,这样在将driver注册到内核中时,内核设备模型部分的代码(driver/base/driver.c)会自动将这些attribute添加到sysfs中。
350 | - p,driver core的私有数据指针,其它模块不能访问。
351 |
352 |
353 |
354 | 有点抽象画个图
355 |
356 | ```text
357 | +-------------+
358 | | Device |
359 | +-------------+
360 | |
361 | +-------------+
362 | | Bus |
363 | +-------------+
364 | |
365 | +--------------+
366 | | Device Driver|
367 | +--------------+
368 | ----------------------分割线---------------------------------------------------------------
369 | +--------------------------+
370 | | Device (struct device) |
371 | +--------------------------+
372 | |
373 | +--------------------------------+
374 | | Bus Type (struct bus_type) |
375 | +--------------------------------+
376 | |
377 | +--------------|------------------+
378 | | | |
379 | +-----------------+ +-----------------+ +------------------+
380 | | Device Driver | | Device Driver | | Device Driver |
381 | +-----------------+ +-----------------+ +------------------+
382 | | (struct | | (struct | | (struct |
383 | | device_driver)| | device_driver)| | device_driver)|
384 | +-----------------+ +-----------------+ +------------------+
385 | ----------------------分割线---------------------------------------------------------------
386 | +---------------------+
387 | | Device Tree |
388 | +----------+----------+
389 | |
390 | +------------------------|---------------------|------------------------+
391 | | | | |
392 | +------------------+ +------------------+ +------------------+ +------------------+
393 | | Bus Type A | | Bus Type B | | Bus Type C | | Bus Type D |
394 | +------------------+ +------------------+ +------------------+ +------------------+
395 | | | | |
396 | | | | |
397 | +------------------+ +------------------+ +------------------+ +------------------+
398 | |Device A1|Device A2||Device B1 |Device B2| |Device C1 |Device C2|| Device D1 | Device D2|
399 | +------------------+ +------------------+ +------------------+ +------------------+
400 | | | | | | | | |
401 | | | | | | | | |
402 | +------------------+ +------------------+ +------------------+ +------------------+
403 | | Driver A1 |Driver A2|Driver B1 |Driver B2|Driver C1 | Driver C2| Driver D1 | Driver D2|
404 | +------------------+ +------------------+ +------------------+ +------------------+
405 | ```
406 |
407 | - `Device` 表示系统中的实际设备,例如硬件设备或虚拟设备。每个设备都属于一个特定的 `Bus`。
408 | - `Bus` 是设备的集合,它负责管理和组织设备。一个 `Bus` 可以包含多个设备。
409 | - `Device Driver` 是负责与特定设备交互的代码。每个设备都有一个关联的设备驱动程序。
410 |
411 | 其中Device和Driver是会进行match 匹配的。
412 |
413 | 在Linux内核中,如果device和device_driver具有相同的名称,内核就会执行device_driver的probe回调函数,来完成device和device_driver的匹配。
414 |
415 | 执行device_driver的probe回调函数实际上是有bus模块实现的,因为device和driver都挂在在bus总线上,bus总线是最清楚哪个device匹配那个driver。
416 |
417 | 具体可以阅读[Linux设备模型之device和device_driver](https://mp.weixin.qq.com/s/3pGZXwA9WPPMlWZltu5xMg)以及[Linux设备模型之总线bus](https://mp.weixin.qq.com/s/CIp1ykYasQFKuubV8atE0w),讲的比较详细。
418 |
419 |
420 | ## 3.virtio-blk
421 |
422 | 在上面我们了解了`driver`那么我们看一下`virtio_driver`的定义
423 |
424 | ```c
425 | struct virtio_driver {
426 | struct device_driver driver;
427 | const struct virtio_device_id *id_table;
428 | const unsigned int *feature_table;
429 | unsigned int feature_table_size;
430 | const unsigned int *feature_table_legacy;
431 | unsigned int feature_table_size_legacy;
432 | int (*validate)(struct virtio_device *dev);
433 | int (*probe)(struct virtio_device *dev);
434 | void (*scan)(struct virtio_device *dev);
435 | void (*remove)(struct virtio_device *dev);
436 | void (*config_changed)(struct virtio_device *dev);
437 | #ifdef CONFIG_PM
438 | int (*freeze)(struct virtio_device *dev);
439 | int (*restore)(struct virtio_device *dev);
440 | #endif
441 | };
442 | ```
443 |
444 | 其中,我们最关键的函数就是`probe`和`remove` ,并且我们可以看到结构体中包含了`device_driver`所以实际上`virtio_driver`就是`device_driver`
445 |
446 |
447 |
448 | 我们现在把目光放在`virtio_blk.c`上模块加载如下
449 |
450 | ```c
451 | static int __init virtio_blk_init(void)
452 | {
453 | int error;
454 | //分配workqueue
455 | virtblk_wq = alloc_workqueue("virtio-blk", 0, 0);
456 | if (!virtblk_wq)
457 | return -ENOMEM;
458 | //注册新块设备
459 | major = register_blkdev(0, "virtblk");
460 | if (major < 0) {
461 | error = major;
462 | goto out_destroy_workqueue;
463 | }
464 | //注册virtio_driver
465 | error = register_virtio_driver(&virtio_blk);
466 | if (error)
467 | goto out_unregister_blkdev;
468 | return 0;
469 |
470 | out_unregister_blkdev:
471 | unregister_blkdev(major, "virtblk");
472 | out_destroy_workqueue:
473 | destroy_workqueue(virtblk_wq);
474 | return error;
475 | }
476 |
477 | static void __exit virtio_blk_fini(void)
478 | {
479 | unregister_virtio_driver(&virtio_blk);
480 | unregister_blkdev(major, "virtblk");
481 | destroy_workqueue(virtblk_wq);
482 | }
483 | module_init(virtio_blk_init);
484 | module_exit(virtio_blk_fini);
485 | ```
486 |
487 |
488 |
489 | `register_virtio_driver` 中做的事就是设置driver中的bus然后注册驱动
490 |
491 | ```c
492 | int register_virtio_driver(struct virtio_driver *driver)
493 | {
494 | /* Catch this early. */
495 | BUG_ON(driver->feature_table_size && !driver->feature_table);
496 | driver->driver.bus = &virtio_bus;
497 | return driver_register(&driver->driver);
498 | }
499 | ```
500 |
501 | 其中`virtio_bus`的定义如下:
502 |
503 | ```c
504 | static struct bus_type virtio_bus = {
505 | .name = "virtio",
506 | .match = virtio_dev_match,
507 | .dev_groups = virtio_dev_groups,
508 | .uevent = virtio_uevent,
509 | .probe = virtio_dev_probe,
510 | .remove = virtio_dev_remove,
511 | };
512 | ```
513 |
514 | 关于注册中最主要的`virtio_driver` 定义:
515 |
516 | ```c
517 | static struct virtio_driver virtio_blk = {
518 | .feature_table = features,
519 | .feature_table_size = ARRAY_SIZE(features),
520 | .feature_table_legacy = features_legacy,
521 | .feature_table_size_legacy = ARRAY_SIZE(features_legacy),
522 | .driver.name = KBUILD_MODNAME,
523 | .driver.owner = THIS_MODULE,
524 | .id_table = id_table,
525 | .probe = virtblk_probe,
526 | .remove = virtblk_remove,
527 | .config_changed = virtblk_config_changed,
528 | #ifdef CONFIG_PM_SLEEP
529 | .freeze = virtblk_freeze,
530 | .restore = virtblk_restore,
531 | #endif
532 | };
533 | ```
534 |
535 | 我们重点关注`virtblk_probe` 的代码
536 |
537 | ```c
538 | static int virtblk_probe(struct virtio_device *vdev)
539 | {
540 | struct virtio_blk *vblk;
541 | struct request_queue *q;
542 | int err, index;
543 |
544 | // ... (省略参数声明和错误检查)
545 |
546 | // 为块设备分配索引
547 | err = allocate_device_index(&index);
548 | if (err < 0)
549 | goto out;
550 |
551 | // 为 virtio_blk 结构分配内存
552 | vblk = allocate_virtio_blk_structure(vdev);
553 | if (!vblk) {
554 | err = -ENOMEM;
555 | goto out_free_index;
556 | }
557 |
558 | // 初始化 virtio_blk 结构
559 | err = initialize_virtio_blk_structure(vblk);
560 | if (err)
561 | goto out_free_vblk;
562 |
563 | // 初始化块设备的请求队列
564 | q = initialize_request_queue(vblk);
565 | if (IS_ERR(q)) {
566 | err = PTR_ERR(q);
567 | goto out_cleanup_disk;
568 | }
569 |
570 | // 配置块设备的其他参数
571 | configure_block_device_parameters(vdev, vblk, q);
572 |
573 | // 更新块设备容量并标记设备为准备就绪
574 | virtblk_update_capacity(vblk, false);
575 | virtio_device_ready(vdev);
576 |
577 | // 向系统添加块设备
578 | err = add_block_device_to_system(vdev, vblk->disk, virtblk_attr_groups);
579 | if (err)
580 | goto out_cleanup_disk;
581 |
582 | return 0;
583 | ..........
584 | return err;
585 | }
586 | ```
587 |
588 | 做的事大致如下:
589 |
590 | 1. 为块设备分配索引(`allocate_device_index`)
591 | 2. 为 `virtio_blk` 结构分配内存(`allocate_virtio_blk_structure`)
592 | 3. 初始化 `virtio_blk` 结构(`initialize_virtio_blk_structure`)
593 | 4. 初始化块设备的请求队列(`initialize_request_queue`)
594 | 5. 配置块设备的其他参数(`configure_block_device_parameters`)
595 | 6. 更新块设备容量并标记设备为准备就绪(`virtblk_update_capacity` 和 `virtio_device_ready`)
596 | 7. 向系统添加块设备(`add_block_device_to_system`)
597 |
598 | 我们再看一下`virtio_blk` 结构体定义
599 |
600 | ```c
601 | struct virtio_blk {
602 | /*
603 | * This mutex must be held by anything that may run after
604 | * virtblk_remove() sets vblk->vdev to NULL.
605 | *
606 | * blk-mq, virtqueue processing, and sysfs attribute code paths are
607 | * shut down before vblk->vdev is set to NULL and therefore do not need
608 | * to hold this mutex.
609 | */
610 | struct mutex vdev_mutex;
611 | struct virtio_device *vdev;
612 |
613 | /* The disk structure for the kernel. */
614 | struct gendisk *disk;
615 |
616 | /* Block layer tags. */
617 | struct blk_mq_tag_set tag_set;
618 |
619 | /* Process context for config space updates */
620 | struct work_struct config_work;
621 |
622 | /* Ida index - used to track minor number allocations. */
623 | int index;
624 |
625 | /* num of vqs */
626 | int num_vqs;
627 | int io_queues[HCTX_MAX_TYPES];
628 | struct virtio_blk_vq *vqs;
629 | };
630 | ```
631 |
632 | 这里我们在rust实现中就需要实现这个结构体
633 |
634 |
635 |
636 |
637 |
638 | 未完todo。。。。。。
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 | ## 4.rust的相关驱动抽象
647 |
648 |
649 |
650 |
651 |
--------------------------------------------------------------------------------
/优秀文档参考/lcx-Rust-for-Linux-net-driver.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust for Linux驱动模块开发报告-lcx
3 | Date: 2023-11
4 | Tags:
5 | - Author: lcx
6 | - Reports Repo: https://github.com/451846939/TimeMachine/tree/master/src/os/rust-for-linux
7 | ---
8 |
9 | # rust for linux
10 |
11 | ## 1. 背景
12 |
13 | Rust for Linux 这个项目的目的就是为了将 Rust 引入 Linux,让 Rust 成为 C 语言之后的第二语言。但它最初的目的是:实验性地支持Rust来写内核驱动。
14 |
15 | 以往,Linux 内核驱动的编写相对于应用其实是比较复杂的,具体复杂性主要表现在以下两个方面:
16 |
17 | - 编写设备驱动必须了解Linux 内核基础概念、工作机制、硬件原理等知识
18 | - 设备驱动中涉及内存和多线程并发时容易出现 Bug,linux驱动跟linux内核工作在同一层次,一旦发生问题,很容易造成内核的整体崩溃
19 |
20 | 引入 Rust 从理想层面来看,一方面在代码抽象和跨平台方面比 C 更有效,另一方面代码质量会更高,有效减少内存和多线程并发类 Bug 。但具体实践如何,是否真如理想中有效,这就需要后续的实验。
21 |
22 | Rust for Linux 就是为了帮助实现这一目标,为 Linux 提供了 Rust 相关的基础设施和方便编写 Linux 驱动的安全抽象。
23 |
24 | ### [Rust for Linux 第四次补丁提审](https://rustmagazine.github.io/rust_magazine_2022/Q1/contribute/rust-for-linux-clk.html#rust-for-linux-第四次补丁提审)
25 |
26 | 补丁的具体细节可以在Linux 邮件列表中找到:
27 |
28 | - RFC: https://lore.kernel.org/lkml/20210414184604.23473-1-ojeda@kernel.org/
29 | - v1: https://lore.kernel.org/lkml/20210704202756.29107-1-ojeda@kernel.org/
30 | - v2: https://lore.kernel.org/lkml/20211206140313.5653-1-ojeda@kernel.org/
31 | - v3: https://lore.kernel.org/lkml/20220117053349.6804-1-ojeda@kernel.org/
32 |
33 | 第二次补丁改进摘要可参考:[Rust for Linux 源码导读 | Ref 引用计数容器](https://rustmagazine.github.io/rust_magazine_2021/chapter_12/ref.html)。
34 |
35 | **第三次补丁改进摘要:**
36 |
37 | - 对 Rust 的支持有一些改进:
38 | - 升级到 Rust 1.58.0
39 | - 增加了自动检测来判断是否有合适的 Rust 可用工具链(`CONFIG_RUST_IS_AVAILABLE`,用于替换`HAS_RUST`)
40 | - 移除`!COMPILE_TEST`
41 | - 其他构建系统的改进
42 | - 文档改进
43 | - 需要的不稳定功能之一,`-Zsymbol-mangling-version=v0`,在 1.59.0 中变得稳定。另一个,“maybe_uninit_extra”,将在 1.60.0 中。
44 | - 对抽象和示例驱动程序的一些改进:
45 | - 加了将在总线中使用的“IdArray”和“IdTable”,以允许驱动程序指定在编译时保证为零终止(zero-terminated)的设备 ID 表。
46 | - 更新了 `amba` 以使用新的常量设备 ID 表支持。
47 | - 初始通用时钟框架抽象。
48 | - 平台驱动程序现在通过实现特质(trait)来定义。包括用于简化平台驱动程序注册的新宏和新示例/模板。
49 | - `dev_*` 打印宏。
50 | - `IoMem` 的`{read,write}*_relaxed` 方法。
51 | - 通过删除 `FileOpener` 来简化文件操作。
52 | - 在驱动程序注册的参数中添加了“ThisModule”。
53 | - 添加用 Rust 编写的树外 Linux 内核模块的基本模板: [https ://github.com/Rust-for-Linux/rust-out-of-tree-module](https ://github.com/Rust-for-Linux/rust-out-of-tree-module)
54 |
55 | **第四次补丁改进摘要:**
56 |
57 | - 基础设施更新:
58 |
59 | - 整合 CI : 英特尔 0DAY/LKP 内核测试机器人 / kernelCI/ GitHub CI
60 | - 内核模块不需要写 crate 属性,`#![no_std]` 和 `#![feature(...)]` 不再存在,删除样板。
61 | - 添加了单目标支持,包括`.o`、`.s`、`.ll`和`.i`(即宏扩展,类似于 C 预处理源)。
62 | - 对`helpers.c`文件的解释和对helpers的许可和导出的许可。
63 | - 文档Logo现在基于矢量 (SVG)。此外,已经为上游提出了 Tux 的矢量版本,并且用于改进自定义Logo支持的 RFC 已提交至上游 Rust。
64 | - 添加了关于注释 (`//`) 和代码文档的编码指南(`///`)。
65 | - `is_rust_module.sh` 返工。
66 | - 现在跳过了叶子模块的`.rmeta`的生成。
67 | - 其他清理、修复和改进。
68 |
69 | - 抽象和驱动更新:
70 |
71 | - 增加了对静态(全局共享变量)同步原语的支持。`CONFIG_CONSTRUCTORS`被用于实现。
72 |
73 | - 通过使用标记类型简化了锁防护,即`Guard`和`GuardMut`统一为一个参数化的类型。如果标记是 `WriteLock`,那么 `Guard`就会实现`DerefMut`(以前只由`GuardMut`实现)。
74 |
75 | - 可选参数添加到杂项设备(misc device)的注册中。遵循构建者模式,例如:
76 |
77 | ```rust
78 | miscdev::Options::new()
79 | .mode(0o600)
80 | .minor(10)
81 | .parent(parent)
82 | .register(reg, c_str!("sample"), ())
83 | ```
84 |
85 | - 增加了 "RwSemaphore "的抽象,该抽象包裹了C端`struct rw_semaphore`。
86 |
87 | - 新的`mm`模块和VMA抽象(包装C端`struct vm_area_struct`)用于`mmap`。
88 |
89 | - GPIO PL061现在使用最近增加的`dev_*!` Rust宏。
90 |
91 | - 支持`!CONFIG_PRINTK`情况。
92 |
93 | - 其他清理、修复和改进。
94 |
95 | rust驱动和内核的关系正如下图:
96 |
97 | 
98 |
99 | ## 2. 编译
100 |
101 | > https://rust-for-linux.com
102 | >
103 | > https://www.kernel.org/doc/html/next/rust/quick-start.html
104 | >
105 | > https://github.com/Rust-for-Linux/linux
106 |
107 | 首先
108 |
109 | ```shell
110 | git clone https://github.com/Rust-for-Linux/linux.git
111 | cd linux
112 | make LLVM=1 rustavailable
113 | rustup override set $(scripts/min-tool-version.sh rustc)
114 | rustup component add rust-src
115 | cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli
116 | ```
117 |
118 | 根据提示[官方文档](https://www.kernel.org/doc/html/next/rust/quick-start.html) 配置好你的rust环境最重要的是要注意llvm要使用16以上的版本
119 |
120 | 这里给出llvm如何配置apt 以及如何安装clang+llvm等开发环境的文档地址: https://apt.llvm.org
121 |
122 | 基本需要的:
123 |
124 | ```shell
125 | sudo apt-get -y install \
126 | binutils build-essential libtool texinfo \
127 | gzip zip unzip patchutils curl git \
128 | make cmake ninja-build automake bison flex gperf \
129 | grep sed gawk bc \
130 | zlib1g-dev libexpat1-dev libmpc-dev \
131 | libglib2.0-dev libfdt-dev libpixman-1-dev libelf-dev libssl-dev
132 | ```
133 |
134 | ```shell
135 | apt-get install clang-format clang-tidy clang-tools clang clangd libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1 liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev llvm-runtime llvm python3-clang
136 | ```
137 |
138 | llvm和clang请注意版本,如果安装的clang16可能运行文件名字为clang-16 可以使用
139 |
140 | ```she
141 | ln -s /usr/bin/clang-16 /usr/bin/clang
142 | ```
143 |
144 | 然后
145 |
146 | ```shell
147 | make ARCH=arm64 LLVM=1 O=build defconfig
148 |
149 | make ARCH=arm64 LLVM=1 O=build menuconfig
150 | #set the following config to yes
151 | General setup
152 | ---> [*] Rust support
153 | Kernel hacking
154 | ---> Sample kernel code
155 | ---> [*] Rust samples
156 | cd build
157 | make ARCH=arm64 LLVM=1 -j8
158 | ```
159 |
160 | menuconfig 会进入一个菜单选择,记得打开`Rust support` 和`Rust samples`
161 |
162 | 
163 |
164 | ## 3. 自定义内核驱动模块
165 |
166 | 我们可以先前往linux的`samples/rust/` 下可以看到rust的驱动例子,那我们可以仿造来写一个hello_world
167 |
168 | 创建`rust_helloworld.rs`
169 |
170 | ```rust
171 | use kernel::prelude::*;
172 |
173 | module! {
174 | type: RustHelloWorld,
175 | name: "rust_helloworld",
176 | author: "whocare",
177 | description: "hello world module in rust",
178 | license: "GPL",
179 | }
180 |
181 | struct RustHelloWorld {}
182 |
183 | impl kernel::Module for RustHelloWorld {
184 | fn init(_module: &'static ThisModule) -> Result {
185 | pr_info!("Hello World from Rust module");
186 | Ok(RustHelloWorld {})
187 | }
188 | }
189 | ```
190 |
191 | 在rust目录下的Makefile加入`obj-$(CONFIG_SAMPLE_RUST_HELLOWORLD) += rust_helloworld.o`
192 |
193 | ```rust
194 | # SPDX-License-Identifier: GPL-2.0
195 |
196 | obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
197 | obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
198 | // +++++++++++++++ add here
199 | obj-$(CONFIG_SAMPLE_RUST_HELLOWORLD) += rust_helloworld.o
200 | // +++++++++++++++
201 |
202 | subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
203 | ```
204 |
205 | 在Kconfig中加入
206 |
207 | ```Kconfig
208 | # SPDX-License-Identifier: GPL-2.0
209 |
210 | menuconfig SAMPLES_RUST
211 | bool "Rust samples"
212 | depends on RUST
213 | help
214 | You can build sample Rust kernel code here.
215 |
216 | If unsure, say N.
217 |
218 | if SAMPLES_RUST
219 |
220 | config SAMPLE_RUST_MINIMAL
221 | tristate "Minimal"
222 | help
223 | This option builds the Rust minimal module sample.
224 |
225 | To compile this as a module, choose M here:
226 | the module will be called rust_minimal.
227 |
228 | If unsure, say N.
229 |
230 | config SAMPLE_RUST_PRINT
231 | tristate "Printing macros"
232 | help
233 | This option builds the Rust printing macros sample.
234 |
235 | To compile this as a module, choose M here:
236 | the module will be called rust_print.
237 |
238 | If unsure, say N.
239 | // +++++++++++++++ add here
240 | config SAMPLE_RUST_HELLOWORLD
241 | tristate "Print Helloworld in Rust"
242 | help
243 | This option builds the Rust HelloWorld module sample.
244 |
245 | To compile this as a module, choose M here:
246 | the module will be called rust_helloworld.
247 |
248 | If unsure, say N.
249 | // +++++++++++++++
250 | config SAMPLE_RUST_HOSTPROGS
251 | bool "Host programs"
252 | help
253 | This option builds the Rust host program samples.
254 |
255 | If unsure, say N.
256 |
257 | endif # SAMPLES_RUST
258 | ```
259 |
260 | 回到linux目录
261 |
262 | ```shell
263 | make ARCH=arm64 LLVM=1 O=build menuconfig
264 | ```
265 |
266 | 这时候我们可以去
267 |
268 | ```test
269 | Kernel hacking
270 | ---> Sample Kernel code
271 | ---> Rust samples
272 | ---> <*>Print Helloworld in Rust (NEW)
273 | ```
274 |
275 | 打开后
276 |
277 | ```shell
278 | cd build
279 | make ARCH=arm64 LLVM=1 -j8
280 | ```
281 |
282 |
283 |
284 | ## 4. qemu启动写的驱动
285 |
286 | ### 1. 方法1
287 |
288 | 之后需要用qemu来进行内核的运行并且要制作一个initramfs
289 |
290 | 我们需要用到busybox
291 |
292 | 这里我们先下载一个由于写的时候busybox最新版是`1.36.1` 所以下载`1.36.1`
293 |
294 | ```shell
295 | cd ~
296 | wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
297 |
298 | tar -xf busybox-1.36.1.tar.bz2
299 | cd busybox-1.36.1
300 |
301 | make menuconfig ARCH=arm64
302 | # 修改配置,选中如下项目,静态编译
303 | # Settings -> Build Options -> [*] Build static binary (no share libs)
304 |
305 | make -j8
306 |
307 | cp ~/busybox-1.36.1/busybox rust-for-linux/linux/
308 | ```
309 |
310 | 完整的流程[busybox制作initramfs](./使用busybox制作内存文件系统initramfs.md) 在这里我们到这一步暂时就够了 当然你也可以自己从[0构建一个](https://docs.kernel.org/admin-guide/initrd.html)
311 |
312 |
313 |
314 | 这里我们使用rust-for-linux 的rust 分支 .github中workflows来操作
315 |
316 | 在linux源码目录创建一个qemu-ini.sh
317 |
318 | ```shell
319 | #!/bin/sh
320 |
321 | busybox insmod rust_print.ko
322 | busybox rmmod rust_print.ko
323 |
324 | busybox insmod rust_helloworld.ko
325 | busybox rmmod rust_helloworld.ko
326 |
327 | busybox insmod rust_minimal.ko
328 | busybox rmmod rust_minimal.ko
329 |
330 | busybox reboot -f
331 | ```
332 |
333 | 再创建一个qemu-initramfs.desc
334 |
335 | ```shell
336 | dir /bin 0755 0 0
337 | dir /sys 0755 0 0
338 | dir /dev 0755 0 0
339 | file /bin/busybox busybox 0755 0 0
340 | slink /bin/sh /bin/busybox 0755 0 0
341 | file /init ./qemu-init.sh 0755 0 0
342 |
343 | file /rust_minimal.ko build/samples/rust/rust_minimal.ko 0755 0 0
344 | file /rust_print.ko build/samples/rust/rust_print.ko 0755 0 0
345 | file /rust_helloworld.ko build/samples/rust/rust_helloworld.ko 0755 0 0
346 | ```
347 |
348 | 运行
349 |
350 | ```shell
351 | build/usr/gen_init_cpio ./qemu-initramfs.desc > qemu-initramfs.img
352 | ```
353 |
354 | ```shell
355 |
356 | qemu-system-aarch64 \
357 | -kernel build/arch/arm64/boot/Image.gz \
358 | -initrd qemu-initramfs.img \
359 | -M virt \
360 | -cpu cortex-a72 \
361 | -smp 2 \
362 | -nographic \
363 | -vga none \
364 | -no-reboot \
365 | -append 'root=/dev/sda' \
366 | | sed 's:\r$::'
367 | ```
368 |
369 | 可以看到如下输出:
370 |
371 | ```text
372 | ....................
373 | [ 0.381944] No soundcards found.
374 | [ 0.383772] uart-pl011 9000000.pl011: no DMA platform data
375 | [ 0.442688] Freeing unused kernel memory: 2112K
376 | [ 0.443427] Run /init as init process
377 | [ 0.481102] rust_print: Rust printing macros sample (init)
378 | [ 0.481270] rust_print: Emergency message (level 0) without args
379 | [ 0.481333] rust_print: Alert message (level 1) without args
380 | [ 0.481391] rust_print: Critical message (level 2) without args
381 | [ 0.481455] rust_print: Error message (level 3) without args
382 | [ 0.481516] rust_print: Warning message (level 4) without args
383 | [ 0.481577] rust_print: Notice message (level 5) without args
384 | [ 0.481674] rust_print: Info message (level 6) without args
385 | [ 0.481742] rust_print: A line that is continued without args
386 | [ 0.481837] rust_print: Emergency message (level 0) with args
387 | [ 0.481896] rust_print: Alert message (level 1) with args
388 | [ 0.481957] rust_print: Critical message (level 2) with args
389 | [ 0.482021] rust_print: Error message (level 3) with args
390 | [ 0.482102] rust_print: Warning message (level 4) with args
391 | [ 0.482173] rust_print: Notice message (level 5) with args
392 | [ 0.482234] rust_print: Info message (level 6) with args
393 | [ 0.482292] rust_print: A line that is continued with args
394 | [ 0.482453] rust_print: 1
395 | [ 0.482596] rust_print: "hello, world"
396 | [ 0.482884] rust_print: [../samples/rust/rust_print.rs:34] c = "hello, world"
397 | [ 0.483099] rust_print: "hello, world"
398 | [ 0.489795] rust_print: Rust printing macros sample (exit)
399 | [ 0.510844] rust_helloworld: Hello World from Rust module
400 | [ 0.546105] rust_minimal: Rust minimal sample (init)
401 | [ 0.546284] rust_minimal: Am I built-in? false
402 | [ 0.550119] rust_minimal: My numbers are [72, 108, 200]
403 | [ 0.550376] rust_minimal: Rust minimal sample (exit)
404 | [ 0.576403] Flash device refused suspend due to active operation (state 20)
405 | [ 0.576539] Flash device refused suspend due to active operation (state 20)
406 | [ 0.576833] reboot: Restarting system
407 | ```
408 |
409 | 可以看到我们自己写的 Hello World from Rust module已经打印出来了
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 | ### 2. 方法2
418 |
419 | 也可以直接下载一个[debian](https://people.debian.org/~gio/dqib/)的
420 |
421 | debian的镜像解压后可以直接修改 -kernel 为rust-for-linux/linux/build/arch/arm64/boot/Image.gz 以及image.qcow2
422 |
423 | ```shell
424 | qemu-system-aarch64 -machine 'virt' -cpu 'cortex-a57' -m 1G -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22 -kernel Image -initrd initrd -nographic -append "root=LABEL=rootfs console=ttyAMA0"
425 | ```
426 |
427 | 由于image.qcow2是虚拟镜像格式所以挂载需要一点特殊的技巧
428 |
429 | ```shell
430 | modprobe nbd max_part=12
431 | qemu-nbd --connect=/dev/nbd0 image.qcow2
432 | mkdir /mnt/test
433 | mount /dev/nbd0p1 /mnt/test/
434 | cp rust-for-linux/linux/build/samples/rust/rust_helloworld.ko /mnt/test/rust_helloworld.ko
435 | umount /mnt/test
436 | qemu-nbd --disconnect /dev/nbd0
437 | modprobe -r nbd
438 | ```
439 |
440 | 再次运行
441 |
442 | ```shell
443 | qemu-system-aarch64 -machine 'virt' -cpu 'cortex-a57' -m 1G -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22 -kernel rust-for-linux/linux/build/arch/arm64/boot/Image.gz -initrd initrd -nographic -append "root=LABEL=rootfs console=ttyAMA0"
444 | ```
445 |
446 | 之后会进入debian 输入账号密码 root root
447 |
448 | 直接
449 |
450 | ```shell
451 | dmesg -C
452 | insmod /rust_helloworld.ko
453 | #[ 57.144725] rust_helloworld: Hello World from Rust module
454 | dmesg
455 | rmmod rust_helloworld
456 | ```
457 |
458 |
459 |
460 | ## 5.和内核函数互相调用
461 |
462 | 由于rust-for-linux还在发展中有很多内核函数是没有被封装的,利用的是bindgen来自动生成对C(和一些C++)库的Rust FFI绑定
463 |
464 | 所以我们必须要知道rust-for-linux如何调用c
465 |
466 | > https://rust-lang.github.io/rust-bindgen/introduction.html
467 | >
468 | > 这是官方文档
469 | >
470 | > 对于linux是如何使用的可以看
471 | >
472 | > https://github.com/d0u9/Linux-Device-Driver-Rust/blob/master/00_Introduction_to_Rust_Module_in_Linux/03-Rust_build_processes_in_kernel.md
473 |
474 | linux 对于bindgen的使用是命令行的方式使用,并且放在了Makeflie中处理流程如下
475 |
476 | [](https://github.com/d0u9/Linux-Device-Driver-Rust/blob/master/00_Introduction_to_Rust_Module_in_Linux/dependency_graph.png)
477 |
478 | 所以我们怎么使用呢在` /rust/kernel/bindings_helper.h`中添加内核头文件
479 |
480 | 例如:
481 |
482 | ```c
483 | #include
484 | ```
485 |
486 | 之后编写你的c函数比如pci中`pci_set_drvdata` 生成`rust_helper_pci_set_drvdata` rust函数
487 |
488 | 添加在`/rust/kernel/helpers.c`中
489 |
490 | ```c
491 | #include
492 | void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data)
493 | {
494 | pci_set_drvdata(pdev, data);
495 | }
496 | EXPORT_SYMBOL_GPL(rust_helper_pci_set_drvdata);
497 | ```
498 |
499 | 在编译内核的时候会在`/linux/build/rust/bindings`中产生下面2个文件
500 |
501 | - bindings_generated.rs
502 | - bindings_helpers_generated.rs
503 |
504 | 
505 |
506 | 至此可以使用`use kernel::bindings` 来使用`rust_helper_pci_set_drvdata`
507 |
508 |
509 |
510 | ## 6. 模块化驱动开发e1000网卡驱动
511 |
512 | > 我们可以在linux目录之外编写我们的自定义驱动模块,具体参考如下链接
513 | >
514 | > https://github.com/Rust-for-Linux/rust-out-of-tree-module
515 |
516 | ### 0.基础知识
517 |
518 | > 关于e1000在MIT 6.S081中有相关的介绍
519 | >
520 | > https://pdos.csail.mit.edu/6.S081/2020/labs/net.html
521 | >
522 | > https://pdos.csail.mit.edu/6.S081/2020/readings/8254x_GBe_SDM.pdf
523 |
524 |
525 |
526 | 首先我们要了解什么是网卡,网卡和操作系统的交互可以看[参考](./网卡框架.pdf)
527 |
528 | 我们需要了解[e1000](./8254x_GBe_SDM.pdf) 的相关知识,重点看以下:
529 |
530 | - 第2部分是必不可少的,并提供了整个设备的概述。
531 | - 第3.2部分概述了数据包接收的过程。
532 | - 第3.3部分概述了数据包传输,以及第3.4部分。
533 | - 第13部分提供了E1000使用的寄存器的概述。
534 | - 第14部分可能有助于理解我们提供的初始化代码。
535 |
536 |
537 |
538 | ### 1.环境准备
539 |
540 | #### 仓库准备
541 |
542 | 这里我们使用别人已经写过的一些方法仓库不然从0开始写驱动需要进行大量的工作我这里直接fork了一份支持生成外部rust-analyzer
543 |
544 | e1000使用清华os的训练营仓库,只需要填写checkpoint即可,可以省去一些基础工作,不过建议仔细看
545 |
546 | > linux: https://github.com/451846939/rust-for-linux-e1000/tree/rust-e1000
547 | >
548 | > e1000: https://github.com/451846939/e1000-driver/
549 |
550 | ps:linux内核其实有一份c的e1000驱动所以我们还需要关闭他们然后重新编译内核,clang和llvm推荐使用14
551 |
552 | 编译内核的方法和[2.编译](#2)一样只是这里的环境需要修改按照本身的版本进行
553 |
554 | 如果出现了bindgen 0.56.0下载不下来
555 |
556 | 可以去 https://github.com/rust-lang/rust-bindgen.git 下载然后切换分支使用cargo install 以及可以把bindgen-cli的cli去掉如下:
557 |
558 | ```shell
559 | cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen
560 | ```
561 |
562 |
563 |
564 |
565 |
566 | #### 开启代码提示
567 |
568 | linux目录中使用
569 |
570 | ```shell
571 | make LLVM=1 O=build rust-analyzer
572 | ```
573 |
574 | 目录之外如果我们需要开启rust-analyzer提示需要在代码的src平级目录使用
575 |
576 | ```shell
577 | make LLVM=1 -C /mnt/rust-for-linux/linux/build M=$PWD rust-analyzer
578 | ```
579 |
580 | /mnt/rust-for-linux/linux 替换成你自己的linux目录
581 |
582 |
583 |
584 | 在vscode `.vscode/settings.json`中添加如下
585 |
586 | ```json
587 | "rust-analyzer.linkedProjects": [
588 | "${workspaceFolder}/e1000-driver/rust-project.json",
589 | "${workspaceFolder}/linux/build/rust-project.json"
590 | ],
591 | ```
592 |
593 |
594 |
595 | ### 2.完成checkpoint
596 |
597 |
598 |
599 |
600 | 1. 首先在网卡驱动初始化的时候我们需要分配tx_ring和rx_ring的内存空间并返回dma虚拟地址和物理地址
601 |
602 | ```rust
603 | let (tx_ring_vaddr, tx_ring_dma) = kfn.dma_alloc_coherent(alloc_tx_ring_pages);
604 | let (rx_ring_vaddr, rx_ring_dma) = kfn.dma_alloc_coherent(alloc_rx_ring_pages);
605 | ```
606 |
607 | 对于这里我们一定要理解ring
608 |
609 | 
610 |
611 | 2. 接着我们需要分配tx_buffer和rx_buffer的内存空间 并返回dma虚拟地址和物理地址
612 |
613 | ```rust
614 | let (mut tx_mbufs_vaddr, mut tx_mbufs_dma) =kfn.dma_alloc_coherent(alloc_tx_buffer_pages);
615 | let (mut rx_mbufs_vaddr, mut rx_mbufs_dma) =kfn.dma_alloc_coherent(alloc_rx_buffer_pages);
616 | ```
617 |
618 |
619 |
620 | 根据MIT 6.S081的HINT得知
621 |
622 | `e1000_transmit`的流程
623 |
624 | 1. 首先,通过获取E1000_RDT控制寄存器并加一模RX_RING_SIZE,询问E1000下一个等待接收的数据包(如果有)的环索引。
625 | 2. 然后,通过在描述符的状态部分检查E1000_RXD_STAT_DD位来检查是否有新的数据包可用。如果没有,停止。
626 | 3. 否则,将mbuf的`m->len`更新为描述符中报告的长度。使用`net_rx()`将mbuf传递给网络栈。
627 | 4. 然后,使用`mbufalloc()`分配一个新的mbuf以替换刚刚传递给`net_rx()`的mbuf。将其数据指针(`m->head`)编程到描述符中。将描述符的状态位清零。
628 | 5. 最后,更新E1000_RDT寄存器为最后处理的环描述符的索引。
629 |
630 |
631 |
632 | `e1000_recv`流程:
633 |
634 | 1. 通过读取E1000_TDT控制寄存器,询问E1000它期望下一个数据包的TX环索引。
635 | 2. 然后检查环是否溢出。如果在由E1000_TDT索引的描述符中未设置E1000_TXD_STAT_DD,说明E1000尚未完成相应的先前传输请求,因此返回错误。
636 | 3. 否则,使用`mbuffree()`释放从该描述符传输的上一个mbuf(如果有的话)。
637 | 4. 接着,填充描述符。`m->head`指向内存中包的内容,`m->len`是包的长度。设置必要的cmd标志(查看E1000手册中的第3.3节),并储存mbuf的指针以供稍后释放。
638 | 5. 最后,通过将E1000_TDT模TX_RING_SIZE加一来更新环位置。
639 | 6. 如果`e1000_transmit()`成功将mbuf添加到环中,返回0。如果失败(例如,没有可用的描述符传输mbuf),返回-1,以便调用者知道释放mbuf。
640 |
641 |
642 |
643 | 这个mbuffer其实就是ring 里做了一个备份
644 |
645 | 3. 给寄存器设置ring
646 |
647 | ```rust
648 | // set tx descriptor base address and tx ring length
649 | self.regs[E1000_TDBAL].write(self.tx_ring_dma as u32);
650 | self.regs[E1000_TDLEN].write((self.tx_ring.len() * size_of::()) as u32);
651 |
652 | // set rx descriptor base address and rx ring length
653 | self.regs[E1000_RDBAL].write(self.rx_ring_dma as u32);
654 | self.regs[E1000_RDLEN].write((self.rx_ring.len() * size_of::()) as u32);
655 | ```
656 |
657 |
658 |
659 | 4. 设置中断处理
660 |
661 | ```rust
662 |
663 | // Enable interrupts
664 | // step1 set up irq_data
665 | // step2 request_irq
666 | // step3 set up irq_handler
667 | let irq_data = Box::try_new(IrqData {
668 | dev_e1000: data.dev_e1000.clone(),
669 | res: data.res.clone(),
670 | napi: data.napi.clone(),
671 | })?;
672 | let irq_regist = request_irq(data.irq, irq_data)?;
673 | data.irq_handler
674 | .store(Box::into_raw(Box::try_new(irq_regist)?), Ordering::Relaxed);
675 | ```
676 |
677 |
678 |
679 | 5. 收包中断处理
680 |
681 | ```rust
682 | fn handle_rx_irq(dev: &net::Device, napi: &Napi, data: &NetData) {
683 | // Exercise4 Checkpoint 1
684 | let mut packets = 0;
685 | let mut bytes = 0;
686 | let recv_vec: Option>> = {
687 | let mut dev_e1k = data.dev_e1000.lock();
688 | dev_e1k.as_mut().unwrap().e1000_recv()
689 | };
690 | if let Some(vec) = recv_vec {
691 | packets = vec.len();
692 | vec.into_iter().for_each(|packet| {
693 | let mut len = packet.len();
694 | let skb = dev.alloc_skb_ip_align(RXBUFFER).unwrap();
695 | let skb_buf =
696 | unsafe { from_raw_parts_mut(skb.head_data().as_ptr() as *mut u8, len) };
697 | skb_buf.copy_from_slice(&packet);
698 |
699 | skb.put(len as u32);
700 | let protocol = skb.eth_type_trans(dev);
701 | skb.protocol_set(protocol);
702 |
703 | napi.gro_receive(&skb);
704 |
705 | bytes += len;
706 | });
707 | pr_info!("handle_rx_irq {} packets,{} bytes\n", packets, bytes);
708 | } else {
709 | pr_info!("handle_rx_irq no packets\n");
710 | }
711 |
712 | data.stats
713 | .rx_bytes
714 | .fetch_add(bytes as u64, Ordering::Relaxed);
715 | data.stats
716 | .rx_packets
717 | .fetch_add(packets as u64, Ordering::Relaxed);
718 | }
719 | ```
720 |
721 | 首先调用收包函数获取收到的包之后,copy到linux中的核心数据结构skb中设置协议栈后把skb发送到linux网络协议栈,最后更新 `data.stats` 中的 `rx_bytes` 和 `rx_packets` 统计信息
722 |
723 |
724 |
725 | 6. 数据发送
726 |
727 | ```rust
728 | /// Corresponds to `ndo_start_xmit` in `struct net_device_ops`.
729 | fn start_xmit(
730 | skb: &SkBuff,
731 | dev: &Device,
732 | data: ::Borrowed<'_>,
733 | ) -> NetdevTx {
734 | pr_info!("start xmit\n");
735 | // Exercise4 Checkpoint 2
736 | skb.put_padto(bindings::ETH_ZLEN);
737 | let skb_data = skb.len() - skb.data_len();
738 | let skb_data = skb.head_data();
739 | dev.sent_queue(skb.len());
740 |
741 | let mut dev_e1k = data.dev_e1000.lock_irqdisable();
742 | let len = dev_e1k.as_mut().unwrap().e1000_transmit(skb_data);
743 | drop(dev_e1k);
744 | if len < 0 {
745 | pr_warn!("skb packet:{}", len);
746 | return net::NetdevTx::Busy;
747 | }
748 | let bytes = skb.len();
749 | let packets = 1;
750 |
751 | skb.napi_consume(64);
752 | data.stats
753 | .tx_bytes
754 | .fetch_add(bytes as u64, Ordering::Relaxed);
755 |
756 | data.stats.tx_packets.fetch_add(packets, Ordering::Relaxed);
757 | dev.completed_queue(packets as u32, bytes as u32);
758 |
759 | return net::NetdevTx::Ok;
760 | }
761 | ```
762 |
763 | 首先我们要设置以太网帧的最小长度,在设备中记录已发送的数据包长度,调用 `e1000_transmit` 方法,把数据包发送到 E1000 设备, 重新启用中断,如果发送数据包的时候没有成功,可能会存在数据正在接收的情况,所以返回Busy。之后在dev设备中记录已完成的队列和统计信息。
764 |
765 |
766 |
767 | ### 3.验证
768 |
769 | > linux内核其实有一份c的e1000驱动所以我们还需要关闭它们然后重新编译内核
770 | >
771 | > ```shell
772 | > make ARCH=arm64 LLVM=1 O=build menuconfig
773 | > ```
774 | >
775 | > 使用/ 搜索e1000按数字键 就可以直接到达对应的地方,和 e1000的全部关闭然后编译
776 | >
777 | > ```shell
778 | > make ARCH=arm64 LLVM=1 -j8
779 | > ```
780 |
781 |
782 |
783 | 首先编译我们的驱动
784 |
785 | ```shell
786 | cd e1000-driver/src/linux
787 | make KDIR=/mnt/rust-for-linux/linux/build
788 | ```
789 |
790 |
791 |
792 | 这里我们直接使用debian的镜像也就是[4.2中的方法2](#4.2)
793 |
794 | ```shell
795 | qemu-system-aarch64 -machine virt -cpu cortex-a57 -m 1G -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22 -nographic -append "root=LABEL=rootfs console=ttyAMA0" -initrd initrd -device e1000,netdev=net0,bus=pcie.0 -netdev user,id=net0 -kernel ../arch/arm64/boot/Image
796 | ```
797 |
798 |
799 |
800 | ```shell
801 | ip address
802 | ```
803 |
804 | 我们可以看到有
805 |
806 | ```text
807 | 2: eth0: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
808 | link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
809 | ```
810 |
811 | eth0 网卡
812 |
813 | 这时候我们可以用scp 把编译的ko文件复制进来
814 |
815 | ```
816 | scp xxx@ip:/mnt/rust-for-linux/e1000-driver/src/linux/../e1000_for_linux.ko /root/
817 | ```
818 |
819 | 然后禁用我们的eth0
820 |
821 | ```shell
822 | ip link set eth0 down
823 | ```
824 |
825 | 加载我们用rust写的e1000的驱动
826 |
827 | ```shell
828 | insmod e1000_for_linux.ko
829 | ```
830 |
831 | 这时候执行`ip l`会发现多了一个eth1
832 |
833 | 启动eth1
834 |
835 | ```shell
836 | ip link set eth1 up
837 | ```
838 |
839 | 由于没有分配ip所以手动分配ip
840 |
841 | 因为qemu网关是10.0.2.2
842 |
843 | 所以这里分配
844 |
845 | ```shell
846 | ip addr add 10.0.2.20/24 dev eth1
847 | ip route add default via 10.0.2.2 dev eth1
848 | ```
849 |
850 | 之后执行ping
851 |
852 | ```shell
853 | ping 10.0.2.2
854 | ```
855 |
856 | 可以看到如下打印
857 |
858 | ```text
859 | PING 10.0.2.2 (10.0.2.2) 56(84) bytes of data.
860 | [ 336.638258] rust_e1000dev: start xmit
861 | [ 336.638671] rust_e1000dev: Read E1000_TDT = 0x0
862 | [ 336.638733] rust_e1000dev: >>>>>>>>> TX PKT 60
863 | [ 336.638844] rust_e1000dev:
864 | [ 336.638844]
865 | [ 336.639136] rust_e1000dev: handle_irq
866 | [ 336.639289] rust_e1000dev: irq::Handler E1000_ICR = 0x83
867 | [ 336.639680] rust_e1000dev: NapiPoller poll
868 | [ 336.639777] rust_e1000dev: Read E1000_RDT + 1 = 0x0
869 | [ 336.639814] rust_e1000dev: RX PKT 64 <<<<<<<<<
870 | [ 336.640057] rust_e1000dev: e1000_recv
871 | [ 336.640057]
872 | [ 336.640605] rust_e1000dev: handle_rx_irq 1 packets, 64 bytes
873 | [ 336.641801] rust_e1000dev: start xmit
874 | [ 336.641883] rust_e1000dev: Read E1000_TDT = 0x1
875 | [ 336.641890] rust_e1000dev: >>>>>>>>> TX PKT 98
876 | [ 336.641972] rust_e1000dev:
877 | [ 336.641972]
878 | [ 336.642191] rust_e1000dev: handle_irq
879 | [ 336.642366] rust_e1000dev: irq::Handler E1000_ICR = 0x83
880 | [ 336.646625] rust_e1000dev: NapiPoller poll
881 | [ 336.646726] rust_e1000dev: Read E1000_RDT + 1 = 0x1
882 | [ 336.646734] rust_e1000dev: RX PKT 98 <<<<<<<<<
883 | [ 336.646835] rust_e1000dev: e1000_recv
884 | [ 336.646835]
885 | [ 336.647052] rust_e1000dev: handle_rx_irq 1 packets, 98 bytes
886 | 64 bytes from 10.0.2.2: icmp_seq=1 ttl=255 time=11.5 ms
887 | [ 337.641037] rust_e1000dev: start xmit
888 | [ 337.641723] rust_e1000dev: Read E1000_TDT = 0x2
889 | [ 337.641802] rust_e1000dev: >>>>>>>>> TX PKT 98
890 | [ 337.642021] rust_e1000dev:
891 | [ 337.642021]
892 | [ 337.642528] rust_e1000dev: handle_irq
893 | [ 337.649010] rust_e1000dev: irq::Handler E1000_ICR = 0x83
894 | [ 337.650481] rust_e1000dev: NapiPoller poll
895 | [ 337.650852] rust_e1000dev: Read E1000_RDT + 1 = 0x2
896 | [ 337.650886] rust_e1000dev: RX PKT 98 <<<<<<<<<
897 | [ 337.651381] rust_e1000dev: e1000_recv
898 | [ 337.651381]
899 | [ 337.651737] rust_e1000dev: handle_rx_irq 1 packets, 98 bytes
900 | 64 bytes from 10.0.2.2: icmp_seq=2 ttl=255 time=13.1 ms
901 | ```
902 |
903 | 完成了我们的e1000网卡的checkpoint编写
904 |
--------------------------------------------------------------------------------
/优秀文档参考/nnull-block-driver分析-童浩轩.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust for Linux驱动模块开发报告-童浩轩
3 | Date: 2023-11
4 | Tags:
5 | - Author: 童浩轩
6 | - Reports Repo: https://github.com/Tunghohin/rust-for-linux/blob/main/reports/final_report.md
7 | ---
8 |
9 | # null block driver 分析
10 |
11 | 项目源码地址
12 |
13 | > https://github.com/metaspace/linux/tree/null_block-RFC
14 |
15 | ## 什么是 null block driver?
16 |
17 | "Null block driver" 通常是指一个虚拟的块设备驱动程序,它并不代表任何真实的硬件设备,而是用于模拟块设备的行为。在 linux 中,C 版本的 null block driver 在 drivers/block/null_blk 中。
18 |
19 | linux 系统中,块设备系统的结构大概可以看成
20 |
21 |
22 |
23 | 进程操作 filesystem,filesystem
24 |
25 | 操作系统为每个进程分配了一个 staging queue,而 hardware dispatch queues 的数量取决于硬件
26 |
27 | ## 代码分析
28 |
29 | #### struct blk_mq_tag_set
30 |
31 | struct blk_mq_tag_set 是 Linux 内核中用于表示块多队列(blk-mq)层中队列标签集合的数据结构。在 blk-mq 中,队列标签(tag)用于标识和跟踪块设备上发出的 I/O 请求。
32 |
33 | C 版本 struct blk_mq_tag_set
34 |
35 | ```c
36 | struct blk_mq_tag_set {
37 | struct blk_mq_ops *ops; // 操作函数指针
38 | unsigned int flags; // 标志位
39 | unsigned int nr_hw_queues; // 硬件队列数量
40 | unsigned int queue_depth; // 每个队列的深度(即队列标签的数量) */
41 | struct request_queue *queue; // 关联的请求队列
42 | void *driver_data; // 驱动程序私有数据指针
43 | ...
44 | };
45 | ```
46 |
47 | 在 rust 中,一个实用的技巧是,将 C 结构体指针用 rust 的 struct wrap 一层
48 |
49 | ```rust
50 | pub struct TagSet {
51 | inner: UnsafeCell,
52 | _p: PhantomData,
53 | }
54 | ```
55 |
56 | 其中需要注意的点是,特征约束 T 在结构体中没有使用,我们只想让其发挥约束的作用,如果强行创建一个元素占用显然是不优的。但是使用 PhantomData 可以在类型系统中表示这个类型的存在,总的来说,PhantomData 是 Rust 中用于表达类型系统中一些约束、关系或者存在性的一种方式,它在类型层面上提供了一些信息,而不引入实际的运行时开销。
57 |
58 | 注意到,blk_mq_tag_set 中,有个 ops 结构体,里面存了一系列函数指针
59 |
60 |
61 |
62 | 在 rust 中,我们要怎样实现呢?
63 |
64 | /kernel/rust/macro/vtable.rs 中,提供了一个 vtable 宏,我们可以通过 table 宏,将 rust 代码转换成 C 代码。
65 | 一个被标注了 #[vtable] attribute 的 trait,他的实现不应该在 rust 端被调用。
66 |
67 | 使用方法如下(以 commit_rqs 为例)
68 |
69 | ```rust
70 | #[macro::vtable]
71 | pub trait Operations: Sized {
72 | ... //以commit_rqs为例
73 | type HwData: ForeignOwnable;
74 |
75 | ...
76 |
77 | fn commit_rqs(
78 | hw_data: ::Borrowed<'_>,
79 | queue_data: ::Borrowed<'_>,
80 | );
81 | ...
82 | }
83 |
84 | pub(crate) struct OperationsVtable(PhantomData);
85 |
86 | impl OperationsVtable {
87 | ...
88 | unsafe extern "C" fn commit_rqs_callback(hctx: *mut bindings::blk_mq_hw_ctx) {
89 | let hw_data = unsafe { T::HwData::borrow((*hctx).driver_data) };
90 |
91 | // SAFETY: `hctx` is valid as required by this function.
92 | let queue_data = unsafe { (*(*hctx).queue).queuedata };
93 |
94 | // SAFETY: `queue.queuedata` was created by `GenDisk::try_new()` with a
95 | // call to `ForeignOwnable::into_pointer()` to create `queuedata`.
96 | // `ForeignOwnable::from_foreign()` is only called when the tagset is
97 | // dropped, which happens after we are dropped.
98 | let queue_data = unsafe { T::QueueData::borrow(queue_data) };
99 | T::commit_rqs(hw_data, queue_data)
100 | }
101 | ...
102 |
103 | const VTABLE: bindings::blk_mq_ops = bindings::blk_mq_ops {
104 | ...
105 | commit_rqs: Some(Self::commit_rqs_callback),
106 | ...
107 | };
108 |
109 | pub(crate) const unsafe fn build() -> &'static bindings::blk_mq_ops {
110 | &Self::VTABLE
111 | }
112 | }
113 | ```
114 |
115 | 其中 types::ForeignOwnable 表示可用于 C 于 rust 之间的资源所有权转移
116 |
117 | ### bio
118 |
119 | 在 Linux 内核中,struct bio 是用于表示块 I/O 操作的结构体。这个结构体包含了有关块 I/O 操作的重要信息,比如要执行的操作、数据缓冲区的位置等。
120 |
121 | struct bio 通常涉及到一个或多个 struct bio_vec,它们描述了块 I/O 操作的数据块的位置和大小。struct bio_vec 中包含了指向数据缓冲区的指针、偏移量和长度。
122 |
123 | ```c
124 | struct bio {
125 | struct bio *bi_next; // 链表中的下一个 bio 结构体
126 | struct block_device *bi_bdev; // 目标块设备
127 | unsigned long bi_flags; // 标志位,用于描述 I/O 操作的状态
128 | bio_flags_t bi_rw; // 读写标志位,指示是读操作还是写操作
129 | unsigned short bi_vcnt; // 向量个数,用于描述 bio 中的数据块数量
130 | unsigned short bi_idx; // 当前处理的数据块索引
131 | unsigned int bi_size; // 数据块大小
132 | unsigned int bi_iter.bi_sector; // 起始扇区号
133 | struct bio_vec bi_io_vec; // 包含数据缓冲区的信息
134 | struct bio_set *bi_pool; // 内存池指针
135 | ...
136 | };
137 | ```
138 |
139 | 在 rust 里,依然采用指针的形式对其进行封装,但是值得注意的是,采用了 NonNull 指针,而非裸指针
140 |
141 | ```rust
142 | pub struct Bio<'a>(
143 | NonNull,
144 | core::marker::PhantomData<&'a ()>,
145 | );
146 | ```
147 |
148 | NonNull 通常与裸指针一起使用,把裸指针 wrap 一层,NonNull 在类型层面上确保了指针非空,减少了犯错的概率。
149 |
150 | bio 类似链表节点,我们可以对其进行 Iterator trait 的封装,使迭代更安全。
151 |
152 | ```rust
153 | pub struct BioIterator<'a> {
154 | pub(crate) bio: Option>,
155 | }
156 |
157 | impl<'a> core::iter::Iterator for BioIterator<'a> {
158 | type Item = Bio<'a>;
159 |
160 | #[inline(always)]
161 | fn next(&mut self) -> Option> {
162 | if let Some(current) = self.bio.take() {
163 | self.bio = current.next();
164 | Some(current)
165 | } else {
166 | None
167 | }
168 | }
169 | }
170 | ```
171 |
172 | ### 驱动
173 |
174 | 待续
175 |
--------------------------------------------------------------------------------
/优秀文档参考/readme.md:
--------------------------------------------------------------------------------
1 | ## 优秀文档参考
2 | 优秀文档参考
3 |
--------------------------------------------------------------------------------
/优秀文档参考/ye-junzhe-叶钧喆-Rust_for_Linux驱动模块开发.md:
--------------------------------------------------------------------------------
1 | ---
2 | Title: Rust for Linux驱动模块开发报告-叶钧喆
3 | Date: 2023-11
4 | Tags:
5 | - Author: 叶钧喆
6 | - Reports Repo: https://github.com/ye-junzhe/rust-for-linux/blob/main/README.md
7 | ---
8 |
9 | # rust-for-linux
10 |
11 |
12 | - [rust-for-linux](#rust-for-linux)
13 | - [Env](#env)
14 | - [编译内核(Exercise 1)](#编译内核exercise-1)
15 | - [编译Rust模块helloworld(Exercise 2)](#编译rust模块helloworldexercise-2)
16 | - [安装QEMU](#安装qemu)
17 | - [利用busybox制作文件系统](#利用busybox制作文件系统)
18 | - [e1000网卡驱动(Exercise 3)](#e1000网卡驱动exercise-3)
19 | - [Preferences](#preferences)
20 | - [先学习编译fujita e1000,了解busybox加载模块的流程](#先学习编译fujita-e1000了解busybox加载模块的流程)
21 | - [编译rust_e1000.ko](#编译ruste1000ko)
22 | - [Linux kernel网络参数设置](#linux-kernel网络参数设置)
23 | - [QEMU启动参数](#qemu启动参数)
24 | - [作业3 e1000-driver 添加代码](#作业3-e1000-driver-添加代码)
25 | - [e1000网卡驱动(Exercise 4)](#e1000网卡驱动exercise-4)
26 | - [停用内置e1000网卡驱动](#停用内置e1000网卡驱动)
27 | - [作业4添加代码](#作业4添加代码)
28 | - [实习项目](#实习项目)
29 | - [适配Rust for Linux内核](#适配rust-for-linux内核)
30 | - [对image做处理来适配QEMU启动](#对image做处理来适配qemu启动)
31 | - [fdisk显示img信息](#fdisk显示img信息)
32 | - [计算第一个偏移量,mount img1](#计算第一个偏移量mount-img1)
33 | - [计算第二个偏移量,mount img2](#计算第二个偏移量mount-img2)
34 | - [可以看到运行在6.7.0的Linux内核上](#可以看到运行在670的linux内核上)
35 | - [实现Rust Uart串口驱动](#实现rust-uart串口驱动)
36 |
37 |
38 | ## Env
39 |
40 | - Debian GNU/Linux 12 (bookworm) aarch64 in Parallel Desktop Virtual Machine
41 | - QEMU qemu-system-aarch64 version 7.2.5
42 | - rust-for-linux arm64 6.6.0-rc4
43 | - BusyBox v1.35.0 (Debian 1:1.35.0-4+b3) multi-call binary.
44 |
45 |
46 | ## 编译内核(Exercise 1)
47 |
48 | 使用fujita的linux分支
49 |
50 | ```bash
51 | git clone https://github.com/fujita/linux -b rust-dev --depth=1
52 | ```
53 |
54 | - https://github.com/rcore-os/rust-for-linux/blob/main/exercise1.md
55 |
56 | 按照指导安装依赖,
57 | - 但是注意cargo install bindgen,没有后面的cli,因为fujiya使用的是比较早的linux kernel版本
58 |
59 | ```bash
60 | make ARCH=arm64 LLVM=1 O=build defconfig
61 |
62 | make ARCH=arm64 LLVM=1 O=build menuconfig
63 | #set the following config to yes
64 | General setup
65 | ---> [*] Rust support
66 |
67 | make ARCH=arm64 LLVM=1 -j8
68 | ```
69 |
70 |
71 |
72 | ## 编译Rust模块helloworld(Exercise 2)
73 |
74 | ### 安装QEMU
75 |
76 | ```bash
77 | sudo apt install qemu-user qemu-system-arm # 安装qemu 7.x.x
78 | ```
79 |
80 | > qemu 8.x.x以上运行DQIB kernel会报错 => "...net user not compiled into kernel..."
81 |
82 | ### 利用busybox制作文件系统
83 |
84 | - [Debian Quick Image Baker(DQIB)](https://people.debian.org/~gio/dqib/)
85 | 另一个选择,但是不是很会用
86 |
87 | ```bash
88 | make menuconfig ARCH=arm64
89 |
90 | Settings --->
91 | --- Build Options
92 | [*] Build static binary (no shared libs)
93 |
94 | make -j20
95 | make install
96 | ```
97 |
98 | `cat qemu-init.sh`
99 |
100 | ```bash
101 | #!/bin/sh
102 | busybox echo "[INFO] Init from a minimal initrd!"
103 | busybox echo "============================"
104 | busybox poweroff -f
105 | ```
106 |
107 | `cat qemu-initramfs.desc`
108 |
109 | ```bash
110 | dir /bin 0755 0 0
111 | file /bin/busybox busybox 0755 0 0
112 | slink /bin/sh /bin/busybox 0755 0 0
113 | file /init qemu-init.sh 0755 0 0
114 | ```
115 |
116 | ```bash
117 | # 制作img
118 | # 每次修改完qemu-init.sh & qemu-initramfs.desc要记得重新制作
119 | ../linux-fujita/build/usr/gen_init_cpio qemu-initramfs.desc > qemu-initramfs.img
120 | ```
121 |
122 | - https://github.com/rcore-os/rust-for-linux/blob/main/exercise2.md
123 |
124 | 按照指导添加代码
125 |
126 | ```bash
127 | Kernel hacking
128 | ---> Sample Kernel code
129 | ---> Rust samples
130 | ---> Print Helloworld in Rust (NEW) # 其它也要勾选
131 | ```
132 | `cat qemu-init.sh`
133 |
134 | ```bash
135 | #!/bin/sh
136 | busybox echo "[INFO] Init from a minimal initrd!"
137 | busybox echo "============================"
138 | busybox insmod rust_helloworld.ko
139 |
140 | busybox poweroff -f
141 | ```
142 |
143 | `cat qemu-initramfs.desc`
144 |
145 | ```bash
146 | dir /bin 0755 0 0
147 | file /bin/busybox busybox 0755 0 0
148 | slink /bin/sh /bin/busybox 0755 0 0
149 | file /init qemu-init.sh 0755 0 0
150 | file /rust_helloworld.ko ../linux-fujita/build/samples/rust/rust_helloworld.ko 0755 0 0
151 | ```
152 |
153 | - QEMU启动参数
154 |
155 | ```bash
156 | #!/bin/sh
157 |
158 | qemu-system-aarch64 \
159 | -machine 'virt' \
160 | -cpu 'cortex-a57' \
161 | -m 1G \
162 | -netdev user,id=net,hostfwd=tcp::2222-:22 \
163 | -kernel /home/junzhe/Dev/Software/linux-fujita/build/arch/arm64/boot/Image \
164 | -initrd ../busybox/qemu-initramfs.img \
165 | -nographic \
166 | -append "root=LABEL=rootfs console=ttyAMA0" \
167 | ```
168 |
169 |
170 |
171 |
172 | ## e1000网卡驱动(Exercise 3)
173 |
174 | - https://github.com/rcore-os/rust-for-linux/blob/main/exercise3.md
175 |
176 | ### Preferences
177 |
178 | - https://github.com/fujita/rust-e1000
179 | - https://github.com/fujita/linux/tree/rust-e1000
180 |
181 |
182 |
183 | ### 先学习编译fujita e1000,了解busybox加载模块的流程
184 |
185 | #### 编译rust_e1000.ko
186 |
187 | ```bash
188 | git clone https://github.com/fujita/rust-e1000
189 | make KDIR=/home/junzhe/Dev/Software/linux-fujita/build/ LLVM=1
190 | ```
191 |
192 | `cat qemu-initramfs.desc`
193 |
194 | ```bash
195 | dir /bin 0755 0 0
196 | file /bin/busybox busybox 0755 0 0
197 | slink /bin/sh /bin/busybox 0755 0 0
198 | file /init qemu-init.sh 0755 0 0
199 | file /rust_helloworld.ko ../linux-fujita/build/samples/rust/rust_helloworld.ko 0755 0 0
200 | file /rust_e1000.ko ../rust-e1000/rust_e1000.ko 0755 0 0 # 新增
201 | ```
202 |
203 | #### Linux kernel网络参数设置
204 |
205 | `cat qemu-init.sh`
206 |
207 | ```bash
208 | #!/bin/sh
209 |
210 | busybox echo "[INFO] Init from a minimal initrd!"
211 | busybox echo "============================"
212 | busybox echo "[INFO] Modules Loaded"
213 | busybox insmod rust_helloworld.ko
214 | busybox insmod rust_e1000.ko
215 | busybox echo "============================"
216 | busybox echo "[INFO] Network configured"
217 | busybox echo "IP set to 192.168.100.224"
218 | busybox ifconfig lo 127.0.0.1 netmask 255.0.0.0 up
219 | busybox ifconfig eth0 192.168.100.224 netmask 255.255.255.0 broadcast 192.168.100.255 up
220 | busybox ip addr
221 | busybox echo "============================"
222 |
223 | export 'PS1=(kernel) >'
224 | busybox sh
225 |
226 | busybox poweroff -f
227 | ```
228 |
229 | #### QEMU启动参数
230 |
231 | ```bash
232 | qemu-system-aarch64 \
233 | -machine 'virt' \
234 | -cpu 'cortex-a57' \
235 | -m 1G \
236 | -kernel /home/junzhe/Dev/Software/linux-fujita/build/arch/arm64/boot/Image \
237 | -initrd ../busybox/qemu-initramfs.img \
238 | -netdev tap,ifname=tap0,id=tap0,script=no,downscript=no -device e1000,netdev=tap0 \
239 | -nographic \
240 | -append "root=LABEL=rootfs console=ttyAMA0" \
241 | ```
242 |
243 | - ping通
244 |
245 |
246 |
247 |
248 | ### 作业3 e1000-driver 添加代码
249 |
250 | - https://github.com/yuoo655/e1000-driver/tree/main/src
251 |
252 | - 修改后的代码 https://github.com/ye-junzhe/e1000-driver
253 |
254 | - 增加自定义函数并编译成功
255 |
256 |
257 |
258 | ## e1000网卡驱动(Exercise 4)
259 |
260 | ### 停用内置e1000网卡驱动
261 |
262 | 在menuconfig中搜索e1000,并停用
263 |
264 | ### 作业4添加代码
265 |
266 | - 修改后的代码 https://github.com/ye-junzhe/e1000-driver
267 |
268 | 编译后对e1000模块进行加载
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 | ```bash
277 | busybox insmod e1000_for_linux.ko
278 | busybox ifconfig eth0 up
279 | busybox ifconfig eth0 10.0.2.20
280 | busybox ip route add default via 10.0.2.2 dev eth0
281 | ```
282 |
283 | - ping通外网
284 |
285 |
286 | ## 实习项目
287 |
288 | 在树莓派模拟器上,适配Rust for Linux内核,并实现Rust Uart串口驱动
289 |
290 | ### 适配Rust for Linux内核
291 |
292 | #### 对image做处理来适配QEMU启动
293 |
294 | - 下载Raspberry Pi OS image: https://www.raspberrypi.com/software/operating-systems/
295 |
296 | ##### fdisk显示img信息
297 |
298 |
299 |
300 | ##### 计算第一个偏移量,mount img1
301 |
302 |
303 |
304 | - 由于较新版的Raspberry Pi OS不再支持默认用户名密码登陆,所以需要在镜像根目录添加userconf.txt,手动添加用户,利用openssl生成密码密文
305 |
306 | `echo 'raspberry' | openssl passwd -6 -stdin`
307 |
308 |
309 |
310 | ##### 计算第二个偏移量,mount img2
311 |
312 |
313 |
314 | - 复制出内核vmlinuz(后面会替换成rust-for-linux内核)和文件系统initrd
315 |
316 |
317 |
318 | - 适配rust-for-linux 内核
319 |
320 | ```bash
321 | # 目前最新的
322 | git clone https://github.com/Rust-for-Linux/linux -b rust-dev --depth=1
323 |
324 | make ARCH=arm64 LLVM=1 O=build defconfig
325 |
326 | make ARCH=arm64 LLVM=1 O=build menuconfig
327 | #set the following config to yes
328 | General setup
329 | ---> [*] Rust support
330 |
331 | make ARCH=arm64 LLVM=1 -j8
332 | ```
333 |
334 | - QEMU 运行
335 |
336 | ```bash
337 | qemu-system-aarch64 \
338 | -cpu cortex-a57 \
339 | -M virt \
340 | -m 1G \
341 | -kernel ../linux/build/arch/arm64/boot/Image \
342 | -initrd ../raspberry-pi/initrd.img \
343 | -drive file=../raspberry-pi/2023-10-10-raspios-bookworm-arm64-lite.img,if=none,id=drive0,cache=writeback -device virtio-blk,drive=drive0,bootindex=0 \
344 | -append 'root=/dev/vda2 noresume rw' \
345 | -nographic \
346 | -no-reboot \
347 | ```
348 |
349 | ##### 可以看到运行在6.7.0的Linux内核上
350 |
351 |
352 |
353 | ### 实现Rust Uart串口驱动
354 |
355 | Linux 中的 UART 驱动通常作为内核的一部分提供,而不是独立于内核的用户空间程序。UART 驱动的源代码位于 Linux 内核源代码的 drivers/tty/serial/ 目录下。在这个目录下,你可以找到多个 UART 驱动,每个驱动通常对应于支持的特定硬件。
356 |
357 |
358 |
359 | TODO:
360 | - https://github.com/rcore-os/virtio-drivers
361 |
--------------------------------------------------------------------------------