本文使用qemu运行riscv64 linux旨在与真实硬件板保持一致,因此不使用qemu提供的任何直接加载elf文件的方式启动,而是从头到尾均加载原始的bin文件程序给qemu,使其完整的执行opensbi、u-boot、kernel。如读者想利用qemu加载elf文件直接启动内核,是更为容易的,也不必再阅读本文。
本文Ver1.0编写于2021.6.6,经笔者测试所述编译配置流程均正确,如若出现错误,请检查各组件版本是否和笔者使用的一致。附:各组件版本号为linux-5.10.42,busybox-1.33.1,U-Boot 2021.04,OpenSBI v0.9,qemu-5.2.0,riscv64–glibc–bleeding-edge-2020.08-1
本文Ver1.1更新于2021.6.13,改动如下:1.修改smp参数由4改为8;2.rootfs映像修改为两个分区,并将内核image和fdt单独存放在第一个fat分区内;3.增加对U-Boot的Distro Boot Script支持,现在可以在启动u-boot后自动boot内核,不需要每次手动输入命令;4.增加备注说明提供了使用qemu 9p的虚拟共享目录的方式方便在后续内核开发中交换host和qemu target系统中的文件。
本文Ver1.2更新于2021.6.20,增加如下:1.busybox改为动态链接;2.启动脚本的完善以及多用户管理等
1.编译u-boot,生成u-boot.bin文件
make CROSS_COMPILE=/opt/riscv64--glibc--bleeding-edge-2020.08-1/bin/riscv64-linux- qemu-riscv64_smode_defconfig
make CROSS_COMPILE=/opt/riscv64--glibc--bleeding-edge-2020.08-1/bin/riscv64-linux- -j16
2.编译opensbi,生成fw_jump.bin文件
make CROSS_COMPILE=/opt/riscv64--glibc--bleeding-edge-2020.08-1/bin/riscv64-linux- PLATFORM=generic FW_PAYLOAD_PATH=../u-boot-2021.04/u-boot.bin
3.编译linux内核,生成Image文件
make ARCH=riscv CROSS_COMPILE=/opt/riscv64--glibc--bleeding-edge-2020.08-1/bin/riscv64-linux- defconfig
make ARCH=riscv CROSS_COMPILE=/opt/riscv64--glibc--bleeding-edge-2020.08-1/bin/riscv64-linux- -j16
4.编译busybox,生成最小文件系统所需的应用程序文件
make ARCH=riscv CROSS_COMPILE=/opt/riscv64--glibc--bleeding-edge-2020.08-1/bin/riscv64-linux- menuconfig
make ARCH=riscv CROSS_COMPILE=/opt/riscv64--glibc--bleeding-edge-2020.08-1/bin/riscv64-linux- -j16
make ARCH=riscv CROSS_COMPILE=/opt/riscv64--glibc--bleeding-edge-2020.08-1/bin/riscv64-linux- install
这里我们在menuconfig时候配置下静态编译,这样生成的文件系统内的程序是静态编译的不需要添加动态链接库和动态解释器就可以执行。后文我将给出使用动态可执行程序所需要拷贝的库文件到文件系统的方法。
5.从qemu读出设备树文件qemu-virt.dtb,并转化为源文件qemu-virt.dts
/usr/local/bin/qemu-system-riscv64 \
-M virt,dumpdtb=qemu-virt.dtb \
-m 1G \
-smp 8 \
-kernel u-boot-2021.04/u-boot.bin \
-append "root=/dev/vda2 rw console=ttyS0"
# dtc可以使用内核编译生成的,也可以使用包管理器安装sudo apt-get install device-tree-compiler
dtc -I dtb -O dts -o qemu-virt.dts qemu-virt.dtb
这里写/dev/vda2是因为我们后续制作根文件系统时打算制作两个分区,其中第一个分区存放boot需要的image和fdt,第二个分区为运行时的根文件系统,如果你只打算制作一个分区,请使用/dev/vda
6.制作根文件系统,将编译完成的内核image、设备树文件dtb、busybox文件、以及相关启动脚本文件打包生成rootfs.img文件
# 1.创建用于制作根文件系统的目录
mkdir rootfs
mkdir rootfs/rootfs
mkdir rootfs/bootfs
cd rootfs
# 2.制作虚拟映像文件
dd if=/dev/zero of=rootfs.img bs=1M count=1024
sudo losetup -o 0 --sizelimit 1073741824 /dev/loop70 rootfs.img -P
# 使用fdisk创建两个分区一个fat文件系统,100M,一个ext4文件系统,剩余大小,ubuntu用户建议使用图形界面进行分区更加容易操作
sudo fdisk /dev/loop70
# 3.挂载两个分区
sudo mount /dev/loop70p1 ./bootfs
sudo mount /dev/loop70p2 ./rootfs
# 4.uboot需要的相关文件存入bootfs
sudo cp ./linux-5.10.42/arch/riscv/boot/Image ./bootfs/
sudo cp ../qemu-virt.dtb ./bootfs/
# 创建启动相关的脚本文件,编辑其内容如本文第8节中的内容,如果你不知道为什么填入第8节内容,你可以先填入help命令到这个文件中
sudo touch ./bootfs/boot.cmd
# 执行uboot中的mkimage生成boot.scr
sudo ../u-boot-2021.04/tools/mkimage -A riscv -O linux -T script -C none -a 0 -e 0 -n "Distro Boot Script" -d ./bootfs/boot.cmd ./bootfs/boot.scr
# 5.内核需要的相关文件存入rootfs
sudo cp -r ../busybox-1.33.1/_install/* ./rootfs/
sudo mkdir ./rootfs/boot ./rootfs/etc
sudo mkdir ./rootfs/boot ./rootfs/etc/init.d
# 创建四个启动相关的脚本文件,并增加执行权限,编辑其内容如下文
sudo touch ./rootfs/etc/fstab ./rootfs/etc/inittab ./rootfs/etc/profile ./rootfs/etc/init.d/rcS
sudo chmod +x ./rootfs/etc/fstab ./rootfs/etc/inittab ./rootfs/etc/profile ./rootfs/etc/init.d/rcS
# 6.卸载分区
sudo umount ./rootfs
sudo umount ./bootfs
sudo losetup -d /dev/loop70
proc /proc proc defaults 0 0
none /tmp ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
mdev /dev ramfs defaults 0 0
debugfs /sys/kernel/debug debugfs defaults 0 0
::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
# /etc/profile: system-wide .profile file for the Bourne shells
echo
echo -n "Processing /etc/profile... "
# no-op
echo "Done"
echo
#! /bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LD_LIBRARY_PATH=/lib
export PATH LD_LIBRARY_PATH
mount -a
/sbin/mdev -s
mount -a
echo "--------------------------------------------"
echo " welcome debugging on qemu risc-v 64"
echo "--------------------------------------------"
7.启动qemu
/usr/local/bin/qemu-system-riscv64 \
-M virt \
-m 1G \
-smp 8 \
-nographic \
-bios ./opensbi/build/platform/generic/firmware/fw_jump.bin \
-kernel ./u-boot-2021.04/u-boot.bin \
-drive file=./rootfs/rootfs.img,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0
8.进入uboot后如果你在本文第6节中填入boot.cmd内容为help,则uboot自动boot将失败,你可以执行以下命令加载内核镜像和设备树文件,进入linux内核;如果你已经填入这三条命令到boot.cmd,则不需要任何操作,uboot将自动加载内核。
load virtio 0:1 0x80200000 /Image
load virtio 0:1 0x82000000 /qemu-virt.dtb
booti 0x80200000 - 0x82000000
1.qemu 编译 配置增加./configure --enable-gtk --enable-virtfs后,可以增加一以下host和target命令,用以共享目录交换数据,方便后续开发
/usr/local/bin/qemu-system-riscv64 \
-M virt \
-m 1G \
-smp 4 \
-nographic \
-bios ./opensbi/build/platform/generic/firmware/fw_jump.bin \
-kernel ./u-boot-2021.04/u-boot.bin \
-drive file=./rootfs/rootfs.img,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-fsdev local,security_model=passthrough,id=fsdev0,path=./ \
-device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare
mount -t 9p -o trans=virtio,version=9p2000.L hostshare /mnt/
2.busybox使用动态链接的方法以及内核加载动态链接程序的原理
之前我们使用静态链接的方法是为了快速跑起最小的文件系统,随着后续增加相关用户程序,静态编译的开销将不划算,因此我们这里要重新编译busybox,使用完全的默认的配置动态编译
由于使用了动态编译,因此需要拷贝编译器中的.so库文件到文件系统的lib目录,具体为:编译器下/riscv64–glibc–bleeding-edge-2020.08-1/riscv64-buildroot-linux-gnu/sysroot/lib拷贝到/lib,/riscv64–glibc–bleeding-edge-2020.08-1/riscv64-buildroot-linux-gnu/sysroot/usr/lib拷贝到/usr/lib,创建软连接/lib64到/lib和/usr/lib64到/usr/lib。
动态链接的细节:
待笔者整理补充,后续更新
3.syslogd/klogd的使用
待笔者整理补充,后续更新
4.增加多用户管理以及登录密码等设置
::sysinit:/etc/init.d/rcS
console::respawn:/sbin/getty 38400 console
console::restart:/sbin/init
console::ctrlaltdel:/sbin/reboot
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
sudo:x:27:xiaoming
users:x:100:
nogroup:x:65534:
xiaoming:x:1000:
root:x:0:0:root:/home/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
xiaoming:x:1000:1000:Linux User,,,:/home/xiaoming:/bin/sh
root:8f3SuzAlYA9zc:18802:0:99999:7:::
daemon:*:16092:0:99999:7:::
bin:*:16092:0:99999:7:::
sys:*:16092:0:99999:7:::
nobody:*:16092:0:99999:7:::
xiaoming:8KRJzPtwP/eRQ:18802:0:99999:7:::
mkdir /home
mkdir /home/root
mkdir /home/xiaoming
这三个文件定义了一些用户组用户以及对应的登录密码,xiaoming这个用户是笔者自己加的,你可以改为使用你的用户名称,另外root密码是root,xiaoming的密码是xiaoming。完成以上文件的修改,reboot再次进入系统即可看到用户登录界面。
[SUID]
su = ssx 0.0 # run with euid=0/egid=0
id = ssx 0.0 # run with euid=0/egid=0
halt = ssx 0.0 # run with euid=0/egid=0
reboot = ssx 0.0 # run with euid=0/egid=0
shutdown= ssx 0.0 # run with euid=0/egid=0
passwd = --- 0.0 # disabled for all user except for root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LD_LIBRARY_PATH=/lib
export PATH LD_LIBRARY_PATH
# /etc/profile: system-wide .profile file for the Bourne shells
echo -n "Processing /etc/profile... "
source ~/.bashrc
# no-op
echo "Done"
#! /bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LD_LIBRARY_PATH=/lib
export PATH LD_LIBRARY_PATH
mount -a
/sbin/mdev -s
mount -a
mount -t 9p -o trans=virtio,version=9p2000.L hostshare /mnt/
echo RISCV-QEMU-QQM > /proc/sys/kernel/hostname
/etc/init.d/syslog start
echo "--------------------------------------------"
echo " welcome to RISCV-QEMU-QQM ! "
echo "--------------------------------------------"
5.增加sudo并配置给xiaoming用户
git clone https://github.com/sudo-project/sudo.git
#
# This file MUST be edited with the 'visudo' command as root.
#
# See the sudoers man page for the details on how to write a sudoers file.
#
##
# Override built-in defaults
##
Defaults syslog=auth,runcwd=~
Defaults>root !set_logname
Defaults:FULLTIMERS !lecture,runchroot=*
Defaults:millert !authenticate
Defaults@SERVERS log_year, logfile=/var/log/sudo.log
Defaults!PAGERS noexec
# Host alias specification
# User alias specification
# Cmnd alias specification
# User privilege specification
root ALL=(ALL:ALL) ALL
# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL
chown root:root /usr/bin/sudo && chmod 4755 /usr/bin/sudo
adduser xiaoming sudo
chmod 0440 /etc/sudoers