使用qemu运行riscv64 linux

本文使用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  
  • ./rootfs/etc/fstab
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
  • ./rootfs/etc/inittab
::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
  • ./rootfs/etc/profile
# /etc/profile: system-wide .profile file for the Bourne shells

echo
echo -n "Processing /etc/profile... "
# no-op
echo "Done"
echo
  • ./rootfs/etc/init.d/rcS
#! /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.增加多用户管理以及登录密码等设置

  • 修改文件/etc/inittab内容如下,
::sysinit:/etc/init.d/rcS
console::respawn:/sbin/getty 38400 console
console::restart:/sbin/init
console::ctrlaltdel:/sbin/reboot
  • 创建配置文件/etc/group
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:
  • 创建配置文件/etc/passwd
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
  • 创建配置文件/etc/passwd
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:::
  • 创建用户主目录,注意xiaoming目录以及内容的所属用户改为xiaoming
mkdir /home
mkdir /home/root
mkdir /home/xiaoming

这三个文件定义了一些用户组用户以及对应的登录密码,xiaoming这个用户是笔者自己加的,你可以改为使用你的用户名称,另外root密码是root,xiaoming的密码是xiaoming。完成以上文件的修改,reboot再次进入系统即可看到用户登录界面。

  • 创建配置文件/etc/busybox.conf,之所以创建这个文件是由于busybox带的部分命令如reboot非root用户无法运行,需要增加这个配置才能使用,内容如下:
[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
  • 创建/home/root/.bashrc和/home/xiaoming/.bashrc,内如如下,注意xiaoming目录以及内容的所属用户改为xiaoming
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LD_LIBRARY_PATH=/lib
export PATH LD_LIBRARY_PATH
  • 修改/etc/profile
# /etc/profile: system-wide .profile file for the Bourne shells

echo -n "Processing /etc/profile... "

source ~/.bashrc

# no-op
echo "Done"
  • 修改/etc/init.d/rcS,这里完善一下我们的启动项目,方便使用
#! /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用户

  • 下载sudo源码,切换到最新版本的tag
git clone https://github.com/sudo-project/sudo.git
  • 交叉编译得到sudo程序拷贝到系统的/usr/bin/sudo
  • 创建/etc/sudoers文件
#                                                                         
# 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
  • 执行以下命令,完成最终配置,然后reboot重启系统,使用xiaoming用户登录,尝试sudo确认工作正常
chown root:root /usr/bin/sudo && chmod 4755 /usr/bin/sudo
adduser xiaoming sudo
chmod 0440 /etc/sudoers
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐