之前已经构建了一个内核,并且运行测试通过,不过在我的机器上,需要 30 分钟编译整个过程。这个太久了。考虑到可能会时常编译内核。
所以我打算换一个最小化内核的方式,当然这个内核是不能直接给系统用的,只能用作开发环境。
其实这个过程中最大的问题在于网络与共享,内核是最小化编译的,所以没有带任何网络驱动。好在内核自带了 kvm_config。一键带上 virtio 相关的所有驱动。然后第二个问题就是共享文件,因为我需要编译后扔到 qemu 里面去。
其实一开始想得是,每次都打个 initramfs
。但是想想还是太麻烦了,灵光一闪想到 NFS。结果试了一下 busybox 里面死活挂不上 nfs。搞了几个小时,最后发现是配置写错了。(我特么真想抽死自己)
改了配置发现还是挂不上,才发现还有防火墙和 SELinux 的问题。最后都改完,发现还是挂不上。ORZ...
应该是 busybox 编的有问题,实在不想再去搞这个问题,然后再想想内核带了 nfsroot
参数。回头带上这个参数重新编译了一次。结果每次都卡死在初始化。走投无路去搜了一下,才发现要指明 nfsvers
版本号。
然后还有一个问题就是 kernel space 是 LLVM 编译的,user space 是 gcc 编译的。
然后然后,我试了一下 ban 掉 SELinux,然后系统就起不来了。索性,我就直接回滚快照。没有去找为什么。
然后把之前的步骤重新归纳整理一下。所以下面的内容比较流水帐。
跟着上一篇的内容,编译了 6.3.9 的版本的内核。
最小化内核编译
解压内核
mkdir -p ~/workspace/sources
tar -xvf linux-6.3.9.tar.xz -C ~/workspace/sources
cd ~/workspace/sources/linux-6.3.9
编译最小化内核
mkdir build
make LLVM=1 LLVM_IAS=1 O=build mrproper
make LLVM=1 LLVM_IAS=1 O=build tinyconfig
make LLVM=1 LLVM_IAS=1 O=build kvm_guest.config
这一步会让内核自己根据当前的情况,获取最小化的内核配置。
cd build
make LLVM=1 LLVM_IAS=1 menuconfig
基础配置
64-bit kernel ---> yes
# initrd支持
General setup ---> Initial RAM filesystem and RAM disk (initramfs/initrd) support ---> yes
General setup ---> Configure standard kernel features ---> Enable support for printk ---> yes
# insmod/rmmod/modprobe支持
Enable loadable module support --> yes
Enable loadable module support --> Forced module loading --> yes
Enable loadable module support --> Module unloading --> yes
Executable file formats / Emulations ---> Kernel support for ELF binaries ---> yes
Executable file formats / Emulations ---> Kernel support for scripts starting with #! ---> yes
Executable file formats / Emulations ---> Enable core dump support ---> yes
# /dev文件系统支持
Device Drivers ---> Generic Driver Options ---> Maintain a devtmpfs filesystem to mount at /dev ---> yes
Device Drivers ---> Generic Driver Options ---> Automount devtmpfs at /dev, after the kernel mounted the rootfs ---> yes
Device Drivers ---> Generic Driver Options ---> Use nosuid,noexec mount options on devtmpfs---> yes
# tty支持
Device Drivers ---> Character devices ---> Enable TTY ---> yes
Device Drivers ---> Character devices ---> Serial drivers ---> 8250/16550 and compatible serial support ---> yes
Device Drivers ---> Character devices ---> Serial drivers ---> Console on 8250/16550 and compatible serial port ---> yes
# 基础文件系统支持
File systems ---> Pseudo filesystems ---> /proc file system support ---> yes
File systems ---> Pseudo filesystems ---> sysfs file system support ---> yes
File systems ---> Enable POSIX file locking API --> yes
NFS 配置
General setup ---> Auditing support ---> yes
General setup ---> Configure standard kernel feature ---> Multiple users, groups and capabilities support yes
File systems ---> Network File Systems ---> NFS client support ---> yes
File systems ---> Network File Systems ---> NFS client support for NFS version 2 ---> yes
File systems ---> Network File Systems ---> NFS client support for NFS version 3 ---> yes
File systems ---> Network File Systems ---> NFS client support for NFS version 4 ---> yes
File systems ---> Network File Systems ---> NFS client support for NFSv4.1 ---> yes
File systems ---> Network File Systems ---> NFS client support for NFSv4.2 ---> yes
File systems ---> Network File Systems ---> root file system on NFS ---> yes
File systems ---> Network File Systems ---> NFS: Disable NFS UDP protocol suppor ---> yes
DEBUG 配置
Kernel hacking ---> x86 Debugging ---> Choose kernel unwinder ---> Frame pointer unwinder ---> yes
Kernel hacking ---> Compile-time checks and compiler options ---> Debug information ---> Generate DWARF Version 5 debuginfo ---> yes
Kernel hacking ---> Compile-time checks and compiler options ---> Provide GDB scripts for kernel debugging ---> yes
Kernel hacking ---> Generic Kernel Debugging Instruments ---> Debug Filesystem ---> yes
Kernel hacking ---> Generic Kernel Debugging Instruments ---> Magic SysRq key ---> yes
Kernel hacking ---> Generic Kernel Debugging Instruments ---> Magic SysRq key ---> Enable magic SysRq key over serial ---> yes
Kernel hacking ---> Generic Kernel Debugging Instruments ---> KGDB: kernel debugger ---> KGDB_KDB: include kdb frontend for kgdb -> yes
Kernel hacking ---> Generic Kernel Debugging Instruments ---> KGDB: kernel debugger ---> KGDB: use kgdb over the serial console ---> yes
Kernel hacking ---> Generic Kernel Debugging Instruments ---> KGDB: kernel debugger ---> KGDB_KDB: keyboard as input device ---> yes
Rust 配置
General setup ---> Rust support ---> yes
Kernel hacking ---> Sample kernel code ---> Rust samples ---> Host programs ---> yes
Kernel hacking ---> Sample kernel code ---> Rust samples ---> Minimal ---> M
Kernel hacking ---> Sample kernel code ---> Rust samples ---> Printing macros ---> M
开始编译
make LLVM=1 LLVM_IAS=1 -j$(nproc) bzImage
make LLVM=1 LLVM_IAS=1 -j$(nproc) modules
打包
mkdir ~/workspace/{boot,rootfs}
cp arch/$(uname -m)/boot/bzImage ~/workspace/boot/vmlinuz
make INSTALL_MOD_PATH=~/workspace/rootfs modules_install mod-fw=
编译 Busybox
cd ~/sources
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xvf busybox-1.36.1.tar.bz2
cd busybox-1.36.1/
mkdir build
make O=build defconfig
cd build
make menuconfig
Settings ---> Build Options ---> Build BusyBox as a static binary (no shared libs) ---> yes
make -j$(nproc)
make install
构建 initrd
初始化目录结构
cd ~/workspace/rootfs
mkdir -p bin sbin etc proc sys dev usr/bin usr/sbin
cp -a ~/workspace/sources/busybox-*/build/_install/* .
插入初始化脚本 init
编辑 ~/workspace/rootfs/etc/init.d/rcS
,并赋予执行权限 chmod +x
打包生成 initramfs(可以省略)
find . -print0 | cpio --null -ov --format=newc \
| gzip -9 > ../boot/initramfs.cpio.gz
- 大部分内容来自 Gist @chrisdone
- 在此基础之上新增了 NFS 网络支持与 qemu 相关支持
编译 Rust 驱动
cd ~/workspace/rust-out-of-tree-module/
git clean -xdf
make KDIR=~/workspace/sources/linux-6.3.9/build/ LLVM=1
make -C /home/vagrant/workspace/sources/linux-6.3.9/build/ M=$PWD
make[1]: Entering directory '/home/vagrant/workspace/sources/linux-6.3.9/build'
RUSTC [M] /home/vagrant/workspace/rust-out-of-tree-module/rust_out_of_tree.o
make[2]: Warning: File '/home/vagrant/workspace/rust-out-of-tree-module/modules.order' has modification time 0.15 s in the future
MODPOST /home/vagrant/workspace/rust-out-of-tree-module/Module.symvers
make[2]: warning: Clock skew detected. Your build may be incomplete.
make[2]: Warning: File '/home/vagrant/workspace/rust-out-of-tree-module/rust_out_of_tree.o' has modification time 0.11 s in the future
CC [M] /home/vagrant/workspace/rust-out-of-tree-module/rust_out_of_tree.mod.o
LD [M] /home/vagrant/workspace/rust-out-of-tree-module/rust_out_of_tree.ko
make[2]: warning: Clock skew detected. Your build may be incomplete.
make[1]: Leaving directory '/home/vagrant/workspace/sources/linux-6.3.9/build'
cp -v rust_out_of_tree.ko ~/workspace/rootfs/lib/modules/6.3.9/kernel/rust_out_of_tree.ko
'rust_out_of_tree.ko' -> '/home/vagrant/workspace/rootfs/lib/modules/6.3.9/kernel/rust_out_of_tree.ko'
共享目录启动
由于是开发环境所以需要需要和 kvm 主机共享目录,这里选择使用 nfs 直接把 rootfs 作为 qemu 的根
开启 nfs 服务器
sudo dnf install -y nfs-utils
sudo systemctl enable --now nfs-server rpcbind
sudo firewall-cmd --add-service={nfs,nfs3,mountd,rpc-bind} --permanent
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
sudo setsebool -P nfs_export_all_rw 1 # 开放SELinux权限
编辑/etc/exports
/home/vagrant/workspace/rootfs *(rw,sync,no_subtree_check,all_squash,insecure,fsid=1)
刷新共享
sudo exportfs -avr
开启 nfs 挂载
sudo dnf install -y qemu-kvm-core
host_ip=$(ip addr show eth0 | grep -oP "inet \K\d+\.\d+\.\d+\.\d+")
/usr/libexec/qemu-kvm -kernel ~/workspace/boot/vmlinuz \
-smp 2 \
-cpu host \
-s -S \
-m 512 \
-machine q35 \
-netdev user,id=nic0 -device virtio-net-pci,netdev=nic0,mac=52:54:98:76:54:32 \
-nographic \
-append "console=ttyS0 ip=dhcp root=/dev/nfs nfsroot=$host_ip:/home/vagrant/workspace/rootfs,nfsvers=4 rw"
-
netdev
:这里的 netdev
是代表网卡,网卡的模式使用的是 users
模式,就是那种最常见的与主机共享网络的模式。id
是指明这个 netdev
叫啥。因为后面的 device
要与之绑定。netdev
与 device
是成对出现的。
-
device
:netdev
与 device
是要成对出现的,否则配置不生效。所以这里 device
绑定 nic0
这个 netdev
。第一个参数是指定的网卡类型,这里一般有 e1000
啥的,这里用的是最基本的 virtio-net-pci
。最后一个是 mac
地址,这个 qemu
不会给你生成。要自己写一个。
echo "52:54:$(dd if=/dev/urandom count=1 2>/dev/null |md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\).*$/\1:\2:\3:\4/')"
-
s
与 S
: 表示开放 gdb 调试服务,后续可以通过 target remote :1234
使用 gdb 调试,不进行调试则内核不执行。
-
mahince
:q35
,我不写 q35
它一直给我警告,说默认要 desperated
了。妈蛋,太吵了。
-
append
:内核参数
console
:这里指明 ttyS0
是因为 nographic
参数启动,会默认打在虚拟机的 console
口上。否则就没法看到内容。
ip
: dhcp
,用了 user
模式启动,自动获取 ip
root
:标记使用 nfs
方式挂载根分区。没有一个 /dev/nfs
的设备符,这个只是个标记。
nfsroot
: 指明目标 nfs 服务器的路径与地址。nfsvers
用以指明 nfs 服务器的版本,务必带上 nfsvers
参数,指明 nfs 服务器版本,否则有一定机率卡死。
rw
:以读写的形式挂载内核
gdb 连接并启动
因为这玩意是开了 -S
的,gdb 上去,不 c
一下就一直卡在那
gdb vmlinux
Reading symbols from vmlinux...
(gdb) target remote :1234
Thread 1 received signal SIGTRAP, Trace/breakpoint trap.
0x000000000000fff0 in ?? ()
(gdb) c
Continuing.
回到 qemu,测试一下驱动
modprobe rust_out_of_tree
rust_out_of_tree: loading out-of-tree module taints kernel.
rust_out_of_tree: Rust out-of-tree sample (init)
rmmod rust_out_of_tree
rust_out_of_tree: My numbers are [72, 108, 200]
rust_out_of_tree: Rust out-of-tree sample (exit)
卡死在关机
测完了,poweroff 一下,卡死了,qemu 没有退出。gdb 显示卡死在 hlt
指令之后。
翻了半天,说是 gpio 的问题(好吧不知道这个是个啥)。带上相关配置,依然卡在关机。然后又翻到一个帖子说是 acpi 的问题,才想起来 tinyconfig
没有带上 acpi
配置。
最后附上,acpi
和 gpio
的配置
电源相关配置
Device Drivers ---> Device Tree and Open Firmware support ---> yes
Device Drivers ---> GPIO support ---> yes
Device Drivers ---> GPIO support ---> Virtual GPIO drivers ---> VirtIO GPIO support ---> yes
Device Drivers ---> Board level reset or power off ---> GPIO power-off driver ---> yes
Device Drivers ---> Board level reset or power off ---> GPIO restart driver ---> yes
Device Drivers ---> Board level reset or power off ---> Restart power-off driver ---> yes
Power management and ACPI options ---> ACPI (Advanced Configuration and Power Interface) Support ---> yes
带上新配置重新编译内核,poweroff -f
正常关机。
~~奇怪的是,为什么 poweroff 不能关机呢。感觉是 syscall 的问题,算了后面再说吧。~~搭个环境水了三篇。不出意外下一篇应该还是环境搭建,下次来配置一下 rust-analyser
。然后简单看一下 Rust for linux
是如何工作的。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于