基于qemu-riscv64模拟器运行debian

Debian Port已经可以提供risc-v体系结构的软件包集合[https://wiki.debian.org/RISC-V]。

以下记录基于qemu模拟risc-v平台环境并运行debian的过程。

宿主机环境

宿主机ubuntu18.04.1 LTS(x86_64),安装自带gcc编译器和riscv64交叉编译器。

apt install gcc-riscv64-linux-gnu gcc binutils

qemu

从官网下载并编译riscv64模拟器

git clone https://git.qemu.org/git/qemu.git --depth 1
./configure --target-list=riscv64-softmmu
make

编译后生成模拟器riscv64-softmmu/qemu-system-riscv64。

qemu包含一个称为virt的虚拟平台,通过参数-machine virt(或-M virt)指定使用该类型board以模拟riscv64平台。

注:不必执行make install安装,后面直接在qemu源码根目录下运行,以便于跟踪调试qemu代码。

risc-v opensbi

开源项目opensbi实现符合RISC-V SBI规范的固件firmware,以该firmware(64位)作为引导的第一级。

git clone https://github.com/riscv/opensbi.git --depth 1
make PLATFORM=generic CROSS_COMPILE=riscv64-linux-gnu- PLATFORM_RISCV_XLEN=64

用opensbi自带的测试payload验证qemu和opensbi有效性。

在qemu源码根目录下执行测试脚本:

./riscv64-softmmu/qemu-system-riscv64 -M virt -m 256M -nographic \
    -kernel /root/gitQemu/opensbi/build/platform/generic/firmware/fw_jump.elf \
    -device loader,file=/root/gitQemu/opensbi/build/platform/generic/firmware/payloads/test.bin,addr=0x80200000

结果显示OpenSBI的启动画面,最后一行是测试payload(test.bin)的显示信息”Test payload running”。

u-boot

编译u-boot,作为引导的第二级。

注意:为了让u-boot运行在supervisor mode,选择配置qemu-riscv64_smode_defconfig

git clone https://gitlab.denx.de/u-boot/u-boot.git --depth 1
make qemu-riscv64_smode_defconfig CROSS_COMPILE=riscv64-linux-gnu-
make CROSS_COMPILE=riscv64-linux-gnu-

生成u-boot.bin。现在再次执行如下测试,验证u-boot是否可以被正常启动。

./riscv64-softmmu/qemu-system-riscv64 -machine virt -m 256M -nographic \
    -kernel /root/gitQemu/opensbi/build/platform/generic/firmware/fw_jump.elf \
    -device loader,file=/root/gitQemu/u-boot/u-boot.bin,addr=0x80200000 &

相对于OpenSBI自带的测试,只是更改了-device loader,file=指定的路径。

执行成功将会显示u-boot的启动信息,同时由于没有指定根文件系统,最后会报告找不到启动设备或网络。

debian

从[https://people.debian.org/~gio/dqib/]找到Images for riscv64-virt,下载debian预编译的镜像。

解开压缩包,包括三个文件image.qcow2,initrd和kernel:

其中image.qcow2是完整的debian根文件系统镜像,initrd和kernel是镜像中临时启动盘和内核的拷贝。

通过guestmount挂载该镜像并查看其内容。需要安装libguestfs-tools。

apt install libguestfs-tools

通过guestmount挂载该镜像到/mnt目录,可以查看或修改根文件系统内容,然后用guestunmount解除挂载。

guestmount -a image.qcow2 -m /dev/sda1 /mnt/
ls /mnt
guestunmount /mnt

导出FDT

下步启动内核时需要FDT,从qemu导出。

执行如下脚本:

./riscv64-softmmu/qemu-system-riscv64 -nographic -machine virt,dumpdtb=qemu-virt.dtb -m 1G \
    -kernel /root/gitQemu/opensbi/build/platform/generic/firmware/fw_jump.elf \
    -device loader,file=/root/gitQemu/u-boot/u-boot.bin,addr=0x80200000 \
    -object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-device,rng=rng0 \
    -append "console=ttyS0 rw root=/dev/vda1" \
    -device virtio-blk-device,drive=hd0 -drive file=/root/gitQemu/images/image.qcow2,format=qcow2,id=hd0 \
    -device virtio-net-device,netdev=brnet0,mac=00:16:3e:09:3d:58 -netdev tap,id=brnet0,vhost=on,vnet_hdr=on &

其中起作用的是-machine virt,dumpdtb=qemu-virt.dtb

这样就可以得到qemu自定义的board类型virt的二进制fdt文件qemu-virt.dtb。

构造FIT Image

用u-boot的命令行工具mkimage构造FIT Image并替换debian镜像中的原内核文件vmlinux。

1. 编辑kernel_fdt.its

从u-boot/doc/uImage.FIT/kernel_fdt.its拷贝一份进行修改。

修改内容包括kernel的路径、压缩格式加载地址和入口地址,以及dtb的路径。

修改后内容:

/*
 * Simple U-Boot uImage source file containing a single kernel and FDT blob
 */

/dts-v1/;

/ {
        description = "Simple image with single Linux kernel and FDT blob";
        #address-cells = ;

        images {
                kernel {
                        description = "Linux kernel";
                        data = /incbin/("./kernel");
                        type = "kernel";
                        arch = "riscv";
                        os = "linux";
                        compression = "none";
                        load = ;
                        entry = ;
                        hash-1 {
                                algo = "crc32";
                        };
                        hash-2 {
                                algo = "sha1";
                        };
                };
                fdt-1 {
                        description = "Flattened Device Tree blob";
                        data = /incbin/("./qemu-virt.dtb");
                        type = "flat_dt";
                        arch = "riscv";
                        compression = "none";
                        hash-1 {
                                algo = "crc32";
                        };
                        hash-2 {
                                algo = "sha1";
                        };
                };
        };

        configurations {
                default = "conf-1";
                conf-1 {
                        description = "Boot Linux kernel with FDT blob";
                        kernel = "kernel";
                        fdt = "fdt-1";
                };
        };
};

注意:文件中”./kernel”是从前述debian压缩包中解压得到的kernel文件路径,”./qemu-virt.dtb”是上一步得到的FDT。另外,load和entry也是关键,分别指定了内核加载地址和执行入口地址,这里都是0x81a00000。

执行如下脚本制作FIT Image并替换镜像中的vmlinux:

guestmount -a image.qcow2 -m /dev/sda1 /mnt/
# make sure that the vmlinux and qemu-virt.dtb are all in local directory
../u-boot/tools/mkimage -f ./kernel_fdt.its ./vmlinux-5.5.0-1-riscv64
# override old vmlinux-5.5.0-1-riscv64 in 'image.qcow2'
cp ./vmlinux-5.5.0-1-riscv64 /mnt/boot/vmlinux-5.5.0-1-riscv64
guestunmount /mnt

正式启动debian linux

执行如下脚本,启动qemu模拟riscv64平台,运行debian:

./riscv64-softmmu/qemu-system-riscv64 -nographic -machine virt -m 1G \
    -kernel /root/gitQemu/opensbi/build/platform/generic/firmware/fw_jump.elf \
    -device loader,file=/root/gitQemu/u-boot/u-boot.bin,addr=0x80200000 \
    -object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-device,rng=rng0 \
    -append "console=ttyS0 rw root=/dev/vda1" \
    -device virtio-blk-device,drive=hd0 -drive file=/root/gitQemu/images/image.qcow2,format=qcow2,id=hd0 \
    -device virtio-net-device,netdev=net0,mac=52:54:00:12:34:58 -netdev user,id=net0

与上次执行命令行的区别仅仅是”-machine virt”后面没有指定dumpdtb。

Debian启动之后,用root登录,口令为root。

由于网络类型为user mode,ICMP被禁止,所以无法用ping命令。

直接执行apt测试网络:

apt update

注意:默认的sources.list使用域名deb.debian.org,如果不通,可以更换为镜像ftp.de.debian.org或ftp.kr.debian.org。

11 条回复 A 作者 M 管理员 E
  1. 你好,想请问一下在 “构造FIT Image” 那一步中的 vmlinux-5.5.0-1-riscv64 这个文件是哪里的?

    • 注意这四行的意义:

      guestmount -a image.qcow2 -m /dev/sda1 /mnt/
      第一行:把从debian下载的镜像文件image.qcow2挂到/mnt目录,然后可以在/mnt/boot下看到镜像中包含的内核镜像名是vmlinux-5.5.0-1-riscv64。这个是debian提供的原始vmlinux,它不能直接被u-boot引导,所以咱们需要替换掉它。

      ../u-boot/tools/mkimage -f ./kernel_fdt.its ./vmlinux-5.5.0-1-riscv64
      第二行:用u-boot的工具mkimage生成新的vmlinux,最后一个参数是输出文件名。与前面发现的文件同名,方便咱们下一步直接替换。

      cp ./vmlinux-5.5.0-1-riscv64 /mnt/boot/vmlinux-5.5.0-1-riscv64
      guestunmount /mnt
      第三、四行:完成替换并解除挂载。如此,debian镜像中的vmlinux就可以被u-boot引导了。

    • 非常感谢~按照这个教程,已经可以启动了

  2. 你好,想请问一下在opensbi那一步的地址是如何去设置的。我使用了一样的地址,但是报错:

    Could not open ‘roms/opensbi/build/platform/qemu/virt/firmware/payloads/test.bin,addr=0x80200000’: No s
    uch file or directory

    附:我用您的opensbi方法make的时候发生包括 原因是因为找不到对应的配置文件,但是我在qemu源文件下找到并使用了virt目录 。roms/opensbi/build/platform/qemu/virt

    • 这个错应该是找不到test.bin那个文件。如果你编译opensbi成功,那个文件就在它下面的目录里,你找找看。如果你用roms/opensbi/build/platform/qemu/virt这个目前,先确认该目录下是否存在test.bin这个文件,然后最好用全路径而不是相对路径。

    • 你好,我还是有两个问题需要请教您。
      1.
      我上面没有描述清楚的是,我使用的这个opensbi是qemu自带的,而不是自己下载opensbi源码并且编译的。即在qemu源码目录下的./roms/opensbi/build/platform/qemu/virt。
      当我使用这个自带的opensbi进行测试时,在roms的这个路径下是可以找到test.bin文件的。我使用了全局路径以后可以运行。但是没有出现对应的效果。而是在终端下出现:
      [3]31917

      2.
      另外 , 我使用自带的sbi的原因是,我在直接编译opensbi的过程中提示我找不到对应的配置文件。编译命令和,错误报告如下

      编译命令: make PLATFORM=qemu/virt CROSS_COMPILE=/home/wupf/tools/10August2020/bin/riscv64-unknown-linux-gnu- PLATFORM_RISCV_XLEN=64

      错误报告: make: *** No rule to make target ‘/home/wupf/tools/riscv64-linux/opensbi/platform/qemu/virt/config.mk’. Stop.
      这是因为我git到的opensbi有问题吗?(我的platform下是没有qemu这个文件的)

      我自己又尝试了使用qemu中自带的opensbi下的platform子目录中的qemu
      即qemu-5.1.0-rc3/roms/opensbi/platform/qemu/virt。但这样在编译过程中就报错了。

    • 第一个问题:你找的那个test.bin有可能是可以的,那个提示是表示转后台了,所以看不到显示输出。你把命令行最后的那个’&’去掉,再运行一遍。

      第二个问题:刚才试了一下,发现opensbi中与qemu相关的目录结构有变化,现在对应子目录改名generic。
      现在用这个编译
      make PLATFORM=generic CROSS_COMPILE=riscv64-linux-gnu- PLATFORM_RISCV_XLEN=64

      仅仅把PLATFORM=qemu/virt 改为 PLATFORM=generic

      另外,以后遇到编译问题,建议查看项目的README。该项目README.md中,约148行左右提到
      编译时PLATFORM的可选项,并且以Qemu为例说明应当用generic。通常项目的README会提供重要信息,且是同步的。

    • 谢谢你的回答,但是我仍然有无法解决的问题。并且在网上搜索和查看reamde没有得到解决方案。

      我的错误是在使用了自己编译的opensbi后出现以下错误报告:(并且原先在roms目录下的也出现同样错误)
      rom: requested regions overlap (rom phdr #0: /home/wupf/tools/riscv64-linux/opensbi/build/platform/generic/firmware/fw_jump.elf. free=0x000000008000e240, addr=0x0000000080000000)
      qemu-system-riscv64: rom check and register reset failed

      我尝试过把rom的编译信息清除,也尝试过用rom下的test.bin进行测试。但是都得到了一样的错误。
      我打算从qemu的文档去了解每个过程的意义,仍然谢谢你。!

    • qemu5.1是有这个bug, 但是我用qemu-5.0.0-rc2 能够正常显示。

    • 在上面的问题中,我通过增加一个选项 -bios none 使得程序可以运行。
      但是执行后出现了对应的效果,但是这样也让终端卡在那个界面。

  3. 执行后,出这个panic,具体是什么原因?
    [ 4.277606] Loaded X.509 cert ‘Debian Secure Boot CA: 6ccece7e4c6c0d1f6149f3dd27dfcc5cbb419ea1’
    [ 4.278218] Loaded X.509 cert ‘Debian Secure Boot Signer 2020: 00b55eb3b9’
    [ 4.279146] zswap: loaded using pool lzo/zbud
    [ 4.282731] Key type ._fscrypt registered
    [ 4.282933] Key type .fscrypt registered
    [ 4.283099] Key type fscrypt-provisioning registered
    [ 4.284136] AppArmor: AppArmor sha1 policy hashing enabled
    [ 4.844155] Freeing unused kernel memory: 492K
    [ 4.844985] Run /init as init process
    [ 4.845795] usercopy: Kernel memory overwrite attempt detected to kernel text (offset 507879, size 11)!
    [ 4.846245] ————[ cut here ]————
    [ 4.846435] kernel BUG at mm/usercopy.c:99!
    [ 4.846764] Kernel BUG [#1]
    [ 4.846921] Modules linked in:
    [ 4.847195] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.8.0-1-riscv64 #1 Debian 5.8.7-1
    [ 4.847538] epc: ffffffe0003b5120 ra : ffffffe0003b5120 sp : ffffffe038f73ca0
    [ 4.847796] gp : ffffffe000cc7230 tp : ffffffe038f6a1c0 t0 : ffffffe000cdad48
    [ 4.848059] t1 : 0000000000000064 t2 : 0000000000000000 s0 : ffffffe038f73cf0
    [ 4.848320] s1 : ffffffe00095d740 a0 : 000000000000005b a1 : 0000000000000000
    [ 4.848601] a2 : ffffffe038f73a40 a3 : 0000000000000000 a4 : ffffffe000c1f340
    [ 4.848896] a5 : ffffffe000c1f340 a6 : 000000000000001a a7 : 0000000000000082
    [ 4.849169] s2 : ffffffe000941808 s3 : 000000000007bfe7 s4 : 000000000000000b
    [ 4.849470] s5 : 0000000000000000 s6 : ffffffe00091cbc0 s7 : fffffffffffff000
    [ 4.849742] s8 : 0000003ffffff000 s9 : ffffffe009633e00 s10: 000000000000000b
    [ 4.850012] s11: ffffffe03d668ec0 t3 : 0000000000000000 t4 : 000000000001df48
    [ 4.850279] t5 : ffffffe000cc8510 t6 : ffffffe000cd64aa
    [ 4.850507] status: 0000000000000120 badaddr: 0000000000000000 cause: 0000000000000003
    [ 4.851213] —[ end trace ca4cbd665d16bbff ]—
    [ 4.851545] Kernel panic – not syncing: Attempted to kill init! exitcode=0x0000000b
    [ 4.852063] —[ end Kernel panic – not syncing: Attempted to kill init! exitcode=0x0000000b ]—

欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论