当前位置: 首页 > 工具软件 > OpenSBI > 使用案例 >

QEMU 6.1.0 运行 RISCV64 OpenSBI + U-Boot + Linux

凌照
2023-12-01

QEMU 6.1.0 运行 RISCV64 OpenSBI + U-Boot + Linux

1. 准备

OpenSBI v0.9

git clone https://github.com/riscv-software-src/opensbi.git --branch v0.9

U-Boot v2021.07

https://source.denx.de/u-boot/u-boot.git --branch v2021.07

Linux 5.10.70 (直接在 www.kernel.org 上下载即可)

安装 RISCV 编译工具链 gcc-riscv64-linux-gnu

2. 修改 U-Boot

当使用 QEMU -biso 参数启动 OpenSBI + U-Boot 镜像时,一直出现找不到扁平设备树 (FDT)的情况,目前认为问题出在 U-Boot 函数 boot_get_fdt 中,这个函数位于文件 common/image-fdt.c 中 ,由4个分支进行 FDT 查找:

if (select || genimg_has_config(images)) ...
else if (images->legacy_hdr_valid &&
			image_check_type(&images->legacy_hdr_os_copy,
					 IH_TYPE_MULTI)) ...
else if (genimg_get_format(buf) == IMAGE_FORMAT_ANDROID)
else ...

在最后一个 else 中直接就返回了而没有设置 images 中的 ft_addr 和 ft_len (即 FDT 的地址和长度)。目前没想过度分析,所以直接在最后一个else中补救,原代码如下:

else {
		debug("## No Flattened Device Tree\n");
		goto no_fdt;
}

通过 U-Boot 环境变量来查找 FDT,修改如下:

else {
		fdt_addr = env_get_hex("fdtaddr", 0);
  	if(!fdt_addr) goto no_fdt;
  	fdt_blob = map_sysmem(fdt_addr, 0);
  	if(fdt_check_header(fdt_blob)) goto no_fdt;
}

然后修改 include/configs/qemu-riscv.h 中的默认环境变量,修改 kernel_addr_rfdt_addr_r

"fdt_addr_r=0x82200000"
"kernel_addr_r=0x80200000"

这两个地址是 QEMU 6.1.0 中的默认加载地址,应该是与之前的版本有所变化,U-Boot 2021.07版本还没有更新。因为打算使用 virtio 虚拟块设备的方式挂载文件系统,所以再添加两个启动命令

#define BOOTENV_DEV_ACE(devtypeu, devtypel, instance) \
        "bootcmd_ace=" \
        "if env exists kernel_addr_r; then " \
        "load virtio 0:1 ${kernel_addr_r} /Image; " \
        "booti; "      \
        "fi;\0"

#define BOOTENV_DEV_NAME_ACE(devtypeu, devtypel, instance) \
        "ace "

// 将 BOOTENV ACE 添加到目标设备列表中
#define BOOT_TARGET_DEVICES(func) \
        func(ACE, ace, na)  \            
        func(QEMU, qemu, na) \
        func(VIRTIO, virtio, 0) \
        func(SCSI, scsi, 0) \
        func(DHCP, dhcp, na)

这样一来 U-Boot 应该就没有任何问题了,开始编译

export CROSS_COMPILE=riscv64-linux-gnu- 
make qemu-riscv64_smode_defconfig
make -j16

编译完成后可以得到原材料 u-boot-nodtb.bin
其实在 arch/riscv/lib/image.c 文件中,U-Boot 2021.07 对 RISCV Linux 的头部魔数定义也已经过时,其中 res3 字段目前已经改为魔数 “RISCV”,但是这里并不会对头部检测造成影响(但还是要注意一下),所以目前不去修改。

3. 编译 OpenSBI

特别简单

make PLATFORM=generic CROSS_COMPILE=riscv64-linux-gnu- FW_PATLOAD_PATH=/path/to/u-boot-nodtb.bin

最后得到一个 build/platform/generic/firmware/fw_payload.bin 就是最后的启动镜像。在使用 QEMU 引导镜像时使用 -bios 参数。 在引导过程中 OpenSBI + U-Boot 其实就是发挥着 U-Boot 本身的功能,具体可以查看 OpenSBI 项目的定位。

4. 编译 Linux 内核

特别简单

make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j16

然后从 arch/riscv/boot/ 目录下拿到 Image 作为 Linux 内核镜像。

5. 做一个简单的虚拟块设备吧

来一个空的文件(大小根据实际情况而定)

dd if=/dev/zero of=test.raw

挂载到环回设备上

mknod /dev/loop200 b 7 200
losetup /dev/loop200 ./test.raw

使用 parted 给 test.raw 分区 (分多少个视情况而定)

mklabel gpt 
mkpart linux ext4 0% 100%

映射分区

kpartx -av /dev/loop200

最后各个分区可以在 /dev/mapper 中找到,给分区创建个文件系统

mkfs.ext4 /dev/mapper/loop200p1

随便挂一个目录到上边

mount /dev/mapper/loop200p1 ./door

然后将 Image 复制进去吧,然后用 test.raw 做一个 qcow2 格式的文件给 QEMU 启动用

qemu-img convert -f raw -O qcow2 test.raw test.qcow2

6. 启动 QEMU

qemu-system-riscv64 -machine virt \
	-smp 8   \
	-m 1024  \
	-bios  fw_payload.bin \
	-drive file=./test.qcow2,if=virtio \
	-nographic

观察:

OpenSBI (我在里边打印了点无关紧要的东西,见谅)

OpenSBI v0.9
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

-- XDS(Ace)  


[ACE] <sbi_boot_print_general()> scratch->next_addr:     0x0000000080200000. </qemu/opensbi-0.9/lib/sbi/sbi_init.c><61>
[ACE] <sbi_boot_print_general()> scratch->next_arg1:     0x0000000082200000. </qemu/opensbi-0.9/lib/sbi/sbi_init.c><62>

Platform Name             : riscv-virtio,qemu
Platform Features         : timer,mfdeleg
Platform HART Count       : 8
Firmware Base             : 0x80000000
Firmware Size             : 156 KB
Runtime SBI Version       : 0.2

Domain0 Name              : root
Domain0 Boot HART         : 7
Domain0 HARTs             : 0*,1*,2*,3*,4*,5*,6*,7*
Domain0 Region00          : 0x0000000080000000-0x000000008003ffff ()
Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address      : 0x0000000080200000
Domain0 Next Arg1         : 0x0000000082200000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes

Boot HART ID              : 7
Boot HART Domain          : root
Boot HART ISA             : rv64imafdcsu
Boot HART Features        : scounteren,mcounteren,time
Boot HART PMP Count       : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 54
Boot HART MHPM Count      : 0
Boot HART MHPM Count      : 0
Boot HART MIDELEG         : 0x0000000000000222
Boot HART MEDELEG         : 0x000000000000b109
[ACE] <sbi_hart_switch_mode()> next_mode is PRV_S. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><486>
[ACE] <sbi_hart_switch_mode()> __riscv_xlen is 64. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><519>

U-Boot

U-Boot 2021.07-dirty (Oct 09 2021 - 10:15:26 +0800)

CPU:   rv64imafdcsu
Model: riscv-virtio,qemu
[ACE] <announce_dram_init()> ACE NOTICE <common/board_f.c><213>
DRAM:  1 GiB
Loading Environment from nowhere... OK
In:    uart@10000000
Out:   uart@10000000
Err:   uart@10000000
Net:   No ethernet found.
Hit any key to stop autoboot:  0 
Couldn't find partition virtio 0:1
Can't set block device
image start address  : 0x0000000080200000
magic                : 0xb2830007c29769b0
magic2               : 0x4305db62
Bad Linux RISCV Image magic!

Device 0: 1af4 VirtIO Block Device
            Type: Hard Disk
            Capacity: 321.1 MB = 0.3 GB (657779 x 512)
... is now current device
Scanning virtio 0:1...
Scanning disk virtio-blk#8...
Found 2 disks
No EFI system partition
BootOrder not defined
EFI boot manager: Cannot load any image
scanning bus for devices...

Device 0: unknown device
No ethernet found.
No ethernet found.

此时在 U-Boot 输入 boot 命令,内核便会启动 (我在内核中也打印了些无关紧要的,[ACE] 开头的就是我打印的)

Starting kernel ...

[ACE] <sbi_hart_switch_mode()> next_mode is PRV_S. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><486>
[ACE] <sbi_hart_switch_mode()> __riscv_xlen is 64. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><519>
[ACE] <sbi_hart_switch_mode()> next_mode is PRV_S. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><486>
[ACE] <sbi_hart_switch_mode()> __riscv_xlen is 64. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><519>
[ACE] <sbi_hart_switch_mode()> next_mode is PRV_S. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><486>
[ACE] <sbi_hart_switch_mode()> __riscv_xlen is 64. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><519>
[ACE] <sbi_hart_switch_mode()> next_mode is PRV_S. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><486>
[ACE] <sbi_hart_switch_mode()> __riscv_xlen is 64. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><519>
[ACE] <sbi_hart_switch_mode()> next_mode is PRV_S. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><486>
[ACE] <sbi_hart_switch_mode()> __riscv_xlen is 64. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><519>
[ACE] <sbi_hart_switch_mode()> next_mode is PRV_S. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><486>
[ACE] <sbi_hart_switch_mode()> __riscv_xlen is 64. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><519>
[ACE] <sbi_hart_switch_mode()> next_mode is PRV_S. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><486>
[ACE] <sbi_hart_switch_mode()> __riscv_xlen is 64. </qemu/opensbi-0.9/lib/sbi/sbi_hart.c><519>
[    0.000000] [ACE] init_task pid 0. <init/main.c><872>
[    0.000000] Linux version 5.10.70 (root@4b4eec683270) (riscv64-linux-gnu-gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #1 SMP Fri Oct 8 16:45:57 CST 2021
[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] efi: UEFI not found.
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] software IO TLB: mapped [mem 0x00000000bb1f7000-0x00000000bf1f7000] (64MB)
[    0.000000] SBI specification v0.2 detected
[    0.000000] SBI implementation ID=0x1 Version=0x9
[    0.000000] SBI v0.2 TIME extension detected
[    0.000000] SBI v0.2 IPI extension detected
[    0.000000] SBI v0.2 RFENCE extension detected
[    0.000000] SBI v0.2 HSM extension detected
[    0.000000] riscv: ISA extensions acdfimsu
[    0.000000] riscv: ELF capabilities acdfim
[    0.000000] percpu: Embedded 17 pages/cpu s32552 r8192 d28888 u69632
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 258055
[    0.000000] Kernel command line: noinitrd root=/dev/vda1
[    0.000000] Dentry cache hash table entries: 131072 (order: 8, 1048576 bytes, linear)
[    0.000000] Inode-cache hash table entries: 65536 (order: 7, 524288 bytes, linear)
[    0.000000] Sorting __ex_table...
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 946508K/1046528K available (6969K kernel code, 4109K rwdata, 4096K rodata, 223K init, 340K bss, 100020K reserved, 0K cma-reserved)
[    0.000000] Virtual kernel memory layout:
[    0.000000]       fixmap : 0xffffffcefee00000 - 0xffffffceff000000   (2048 kB)
[    0.000000]       pci io : 0xffffffceff000000 - 0xffffffcf00000000   (  16 MB)
[    0.000000]      vmemmap : 0xffffffcf00000000 - 0xffffffcfffffffff   (4095 MB)
[    0.000000]      vmalloc : 0xffffffd000000000 - 0xffffffdfffffffff   (65535 MB)
[    0.000000]       lowmem : 0xffffffe000000000 - 0xffffffe03fe00000   (1022 MB)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu:     RCU debug extended QS entry/exit.
[    0.000000]  Tracing variant of Tasks RCU enabled.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] riscv-intc: 64 local interrupts mapped
[    0.000000] plic: plic@c000000: mapped 53 interrupts with 8 handlers for 16 contexts.
[    0.000000] [ACE] <start_kernel()> Call init_timers(). <init/main.c><971>
[    0.000000] [ACE] init_timers() Call init_timer_cpus(). <kernel/time/timer.c><2019>
[    0.000000] [ACE] init_timer_cpus() Call init_timer_cpu for cpu:0. <kernel/time/timer.c><2011>
[    0.000000] [ACE] init_timer_cpu() for cpu:0. <kernel/time/timer.c><1994>
[    0.000000] [ACE] init_timer_cpus() Call init_timer_cpu for cpu:1. <kernel/time/timer.c><2011>
[    0.000000] [ACE] init_timer_cpu() for cpu:1. <kernel/time/timer.c><1994>
[    0.000000] [ACE] init_timer_cpus() Call init_timer_cpu for cpu:2. <kernel/time/timer.c><2011>
[    0.000000] [ACE] init_timer_cpu() for cpu:2. <kernel/time/timer.c><1994>
[    0.000000] [ACE] init_timer_cpus() Call init_timer_cpu for cpu:3. <kernel/time/timer.c><2011>
[    0.000000] [ACE] init_timer_cpu() for cpu:3. <kernel/time/timer.c><1994>
[    0.000000] [ACE] init_timer_cpus() Call init_timer_cpu for cpu:4. <kernel/time/timer.c><2011>
[    0.000000] [ACE] init_timer_cpu() for cpu:4. <kernel/time/timer.c><1994>
[    0.000000] [ACE] init_timer_cpus() Call init_timer_cpu for cpu:5. <kernel/time/timer.c><2011>
[    0.000000] [ACE] init_timer_cpu() for cpu:5. <kernel/time/timer.c><1994>
[    0.000000] [ACE] init_timer_cpus() Call init_timer_cpu for cpu:6. <kernel/time/timer.c><2011>
[    0.000000] [ACE] init_timer_cpu() for cpu:6. <kernel/time/timer.c><1994>
[    0.000000] [ACE] init_timer_cpus() Call init_timer_cpu for cpu:7. <kernel/time/timer.c><2011>
[    0.000000] [ACE] init_timer_cpu() for cpu:7. <kernel/time/timer.c><1994>
[    0.000000] [ACE] init_timers() Call open_softirq(TIMER_SOFTIRQ, run_timer_softirq). <kernel/time/timer.c><2022>
[    0.000000] [ACE] <start_kernel()> Call softirq_init(). <init/main.c><974>
[    0.000000] random: get_random_bytes called from start_kernel+0x372/0x4e4 with crng_init=0
[    0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [7]
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns
[    0.000248] sched_clock: 64 bits at 10MHz, resolution 100ns, wraps every 4398046511100ns
[    0.010517] Console: colour dummy device 80x25
[    0.014473] printk: console [tty0] enabled
[    0.018937] Calibrating delay loop (skipped), value calculated using timer frequency.. 20.00 BogoMIPS (lpj=40000)
[    0.019356] pid_max: default: 32768 minimum: 301
[    0.021925] Mount-cache hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.022056] Mountpoint-cache hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.056848] [ACE] rest_init() create kernel_init kernel thread with pid 1. <init/main.c><691>
[    0.057045] [ACE] rest_init() create kernel_init kernel thread on cpu 0. <init/main.c><699>
[    0.057809] [ACE] rest_init() create kthreadd kernel thread with pid 2. <init/main.c><706>
[    0.057922] [ACE] rest_init() create kthreadd kernel thread on cpu 0. <init/main.c><709>
[    0.058094] [ACE] rest_init() call schedule_preempt_disabled() <init/main.c><733>

7. 总结

需要总结的只有 U-Boot 引导 Linux 的过程(这里我的做法很不讲究,只是为了临时看看引导效果才这么干的),使用到distro_bootcmd的方式启动,在修改 U-Boot 时我加入了,自己定义的命令:

bootcmd_ace=if env exists kernel_addr_r; then load virtio 0:1 ${kernel_addr_r} /Image; booti; fi;

其实这里主要就只有两个有用的命令

load virtio 0:1 0x80200000 /Image

这条命令将 virtio 的 0 号设备的第一个分区中的 /Image 文件加载到内存 0x80200000 的位置。这个做法是非常不讲究的,只是为了临时完成一下 Linux 的引导

booti

这个命令用来引导 Image 类型的内核镜像(区别于 zImage,uImage)。booti 会根据 kernel_addr_r 的地址从 U-Boot 跳转到 Linux 内核,

 类似资料: