QEMU做RISC-V开发

工欲善其事,必先利其器

Posted by Fei Wu on January 1, 2024

很多情况QEMU都是系统开发的好选择,甚至时最好的选择。 对于现在的RISC-V开发来说更是如此,各种新的指令集非指令集扩展不停的发布,QEMU是支持的最早基本也是最好的平台。

下载RISC-V镜像

Ubuntu较早支持RISC-V,同时镜像发布比较正规也很容易搜到

https://cdimage.ubuntu.com/ubuntu/releases/23.10/release/

当然我们也可以选择debian,可以从这里下载

https://people.debian.org/~gio/dqib/

我一般并不太关注哪个具体发行版,选择一个好用的就行,特别地,我会选择preinstalled disk image,做到拿来即用。

启动QEMU

正常情况会使用bootloader比如这里的uboot的启动系统。

1
2
3
4
5
qemu-system-riscv64 -machine virt -cpu rv64 -m 1G -nographic \
    -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd \
    -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22 \
    -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.elf \
    -kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf

如果是用作kernel开发的话,在qemu的命令行直接传入kernel image会比较方便。特别地,如果kernel已经builtin了启动qemu所需的驱动,只需要修改这一个地方就可以调试kernel了。

1
2
3
4
5
6
7
qemu-system-riscv64 -machine virt -cpu rv64 -m 1G -nographic \
    -device virtio-blk-device,drive=hd -drive file=image.qcow2,if=none,id=hd \
    -device virtio-net-device,netdev=net -netdev user,id=net,hostfwd=tcp::2222-:22 \
    -bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.elf \
    -kernel $kernel_file \
    -initrd $initrd_file \
    -append "root=LABEL=cloudimg-rootfs ro earlycon"

看到ubuntu login, riscv环境配置就完成了,我们可以用它来调试内核,用户程序,以及固件等。

1
2
3
4
5
6
7
8
9
[  OK  ] Started User Login Management.
[  OK  ] Started Unattended Upgrades Shutdown.
[  OK  ] Started CUPS Scheduler.
[  OK  ] Started Modem Manager.
[  OK  ] Started Disk Manager.

Ubuntu 22.04.2 LTS ubuntu ttyS0

ubuntu login:

简单内核调试

假设我们想要调试kernel的初始化代码,我们在qemu的命令行上加上一下参数, 此时qemu会等待gdb的连接

-s -S

然后用gdb-multi连上并进行调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ gdb-multiarch vmlinux
(gdb) set arch riscv:rv64
The target architecture is set to "riscv:rv64".
(gdb) target remote :1234
Remote debugging using :1234
0x0000000000001000 in ?? ()
(gdb) b start_kernel
Breakpoint 1 at 0xffffffff80c009fa (2 locations)
(gdb) c
Continuing.
[Switching to Thread 1.3]

Thread 3 hit Breakpoint 1, 0xffffffff80c009fa in start_kernel ()
(gdb) b vfs_read
Breakpoint 2 at 0xffffffff802b4be4: vfs_read. (2 locations)
(gdb) c
Continuing.

Thread 3 hit Breakpoint 1, start_kernel () at ../init/main.c:941
941     {
(gdb) bt
#0  start_kernel () at ../init/main.c:941
#1  0xffffffff80001160 in _start_kernel () at ../arch/riscv/kernel/head.S:326
Backtrace stopped: frame did not save the PC
(gdb) p ((struct task_struct *)$tp)->comm
$1 = "swapper\000\000\000\000\000\000\000\000"
(gdb) p ((struct task_struct *)$tp)->pid
$2 = 0