今天的目标是编译并在Qemu上运行kernel。
源码相当大,有4.5G,还需要给生成留出空间,感觉至少得给20G。
编译Qemu
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
pip install sphinx sphinx-rtd-theme
git clone git@github.com:qemu/qemu.git
cd qemu
mkdir build
cd build
../configure --target-list=riscv64-softmmu,riscv64-linux-user,aarch64-softmmu,aarch64-linux-user,x86_64-softmmu,x86_64-linux-user
make -j4
sudo make install 编译BusyBox:
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar xvf busybox-1.36.1.tar.bz2
cd busybox-1.36.1/
make ARCH=arm64 menuconfig -j4
make ARCH=arm64 install -j4
基于BusyBox制作initrd
制作initrd步骤略多。这里make_initrd.sh就是用来自动制作initrd的脚本,内容:
#!/bin/bash
MOUNT_DIR=mnt
CURR_DIR=`pwd`
rm initrd.ext4
dd if=/dev/zero of=initrd.ext4 bs=1M count=32
mkfs.ext4 initrd.ext4
mkdir -p $MOUNT_DIR
mount initrd.ext4 $MOUNT_DIR
cp -arf busybox-1.35.0/_install/* $MOUNT_DIR
cd $MOUNT_DIR
mkdir -p etc dev mnt proc sys tmp mnt etc/init.d/
echo "proc /proc proc defaults 0 0" > etc/fstab
echo "tmpfs /tmp tmpfs defaults 0 0" >> etc/fstab
echo "sysfs /sys sysfs defaults 0 0" >> etc/fstab
echo "#!/bin/sh" > etc/init.d/rcS
echo "mount -a" >> etc/init.d/rcS
echo "mount -o remount,rw /" >> etc/init.d/rcS
echo "echo -e \"Welcome to ARM64 Linux\"" >> etc/init.d/rcS
chmod 755 etc/init.d/rcS
echo "::sysinit:/etc/init.d/rcS" > etc/inittab
echo "::respawn:-/bin/sh" >> etc/inittab
echo "::askfirst:-/bin/sh" >> etc/inittab
chmod 755 etc/inittab
cd dev
mknod console c 5 1
mknod null c 1 3
mknod tty1 c 4 1
cd $CURR_DIR
umount $MOUNT_DIR
echo "make initrd ok!"
然后必须 sudo 执行 sudo ./make_inird.sh
接着,Qemu启动!
qemu-system-aarch64 \
-nographic \
-M virt \
-cpu cortex-a57 \
-smp 2 \
-m 4G \
-kernel /mnt/sdb/dev/linux/build/arch/arm64/boot \
-append "nokaslr root=/dev/ram init=/linuxrc console=ttyS0" \
-initrd disk.img果然第一次就失败了。似乎是文件系统没做对
仔细查看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,所以运行命令修改为
#!/bin/bash
qemu-system-aarch64 \
-nographic \
-M virt \
-cpu cortex-a57 \
-smp 2 \
-m 4G \
-kernel /mnt/sdb/dev/linux/build/arch/arm64/boot/Image \
-append "nokaslr root=/dev/sda init=/linuxrc console=ttyAMA0 console=ttyS0" \
-hda disk.ext3运行,依然报错:
[ 0.927382] uart-pl011 9000000.pl011: no DMA platform data
[ 0.932510] /dev/root: Can't open blockdev
[ 0.933619] VFS: Cannot open root device "/dev/sda" or unknown-block(0,0): error -6
[ 0.933919] Please append a correct "root=" boot option; here are the available partitions:
[ 0.934481] fe00 32768 vda
[ 0.934649] driver: virtio_blk
[ 0.934939] 1f00 131072 mtdblock0
[ 0.934963] (driver?)
可以看出是root不对,应该改成vda:
#!/bin/bash
qemu-system-aarch64 \
-nographic \
-M virt \
-cpu cortex-a57 \
-smp 2 \
-m 4G \
-kernel /mnt/sdb/dev/linux/build/arch/arm64/boot/Image \
-append "nokaslr root=/dev/vda init=/linuxrc console=ttyAMA0 console=ttyS0" \
-hda disk.ext3再次运行:
成功!
今天写一个驱动的helloworld。
首先为kernel添加rust-analyzer支持。在源码根目录执行:
make ARCH=arm64 LLVM=1 rust-analyzer会在build目录生成rust-project.json,在.vscode的settings.json添加如下:
{
"rust-analyzer.linkedProjects": [
"./build/rust-project.json"
]
}就可以为源码添加提示啦。
之后如教程所讲,添加rust_helloworld模块,编译后挂载文件系统,找个文件夹放进去,启动qemu,日志等级调成info: dmesg -n 7,加载驱动,成功。
休息一天。
开始做e1000驱动,首先尝试使用fujita的kernel fork,发现他的版本较低,用的rust版本为1.66,本着学习就学最新的原则,准备直接用官方最新版本修改使用。
fork了官方版本,发现rust只支持x86,对比了rust-dev分支,将修改支持arm64的部分拷贝过来,编译并运行成功。
对比fujita的分支,补全官方没有的API。
补了一天,发现需要改动的太多,6.6 的api也有部分变动,后面的实验也不用这个项目,所以还是老实用fujita的吧。
经过前两天对内核源码的捣鼓,有了些许了解,编译kernel,修改代码,练习3轻松完成。
使用busybox需要手动添加/etc/network/interfaces文件:
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
参考e1000驱动源码,填写练习4内容,始终无法获取网络,编译了原版e1000驱动测试,也不成功,看来是qemu配置问题。
到处搜索资料并测试不同配置,发现问题出在没有配置本地回环地址,执行以下语句:
# 添加本地回环
ifconfig lo 127.0.0.1
# 启动网卡
ifconfig eth0 up成功ping通。
riscv环境配置
busybox 编译
make O=build_riscv ARCH=riscv64 CROSS_COMPILE=riscv64-linux-gnu- menuconfig -j4
# setting 中打开 build static
make O=build_riscv ARCH=riscv64 CROSS_COMPILE=riscv64-linux-gnu- install -j4linux 编译
make LLVM=1 ARCH=riscv defconfig -j4
make LLVM=1 ARCH=riscv menuconfig -j4
make LLVM=1 ARCH=riscv rust-analyzer
bear -- make LLVM=1 ARCH=riscv -j4 给 rust-dev 添加riscv支持:
# scripts/Makefile
# 22行添加,使其生成target 配置
ifdef CONFIG_RISCV
always-$(CONFIG_RUST) += target.json
filechk_rust_target = $< < include/config/auto.conf
$(obj)/target.json: scripts/generate_rust_target include/config/auto.conf FORCE
$(call filechk,rust_target)
endif# scripts/generate_rust_target.rs
else if cfg.has("RISCV") {
if cfg.has("64BIT") {
ts.push("arch", "riscv64");
ts.push("data-layout", "e-m:e-p:64:64-i64:64-i128:128-n64-S128");
ts.push("llvm-target", "riscv64-linux-gnu");
ts.push("target-pointer-width", "64");
} else {
ts.push("arch", "riscv32");
ts.push("data-layout", "e-m:e-p:32:32-i64:64-n32-S128");
ts.push("llvm-target", "riscv32-linux-gnu");
ts.push("target-pointer-width", "32");
}
ts.push("code-model", "medium");
ts.push("disable-redzone", true);
let mut features = "+m,+a".to_string();
if cfg.has("RISCV_ISA_C") {
features += ",+c";
}
ts.push("features", features);
}# arch/riscv/Makefile
KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json
尝试编译,成功。

