使用 Opensbi 引导自己的操作系统

之前在 使用uboot引导自己的操作系统 中尝试了使用 opensbi 来引导自己的操作系统,但是当时发现无论是制作 fip 时还是 ATF 启动时都对 bl33 的镜像有格式要求。

这段时间学习了下 riscv 的汇编和链接脚本,然后重新看了下 ATF 和 uboot 的代码,发现制作自己的 bl33 镜像其实很简单,只要在程序的最开始处加入以下信息即可:

_start:
	/* BL33 information */
	j real_start
	.balign 4
	.word 0x33334c42  /* b'BL33' */
	.word 0xdeadbeea  /* CKSUM */
	.word 0xdeadbeeb  /* SIZE */
	.quad 0x80200000  /* RUNADDR */
	.word 0xdeadbeec
	.balign 4
	j real_start
	.balign 4
	/* BL33 end */

以下是一个简单的例子:

若没有编译过 sdk 的话需要先编译下 fsbl 来获得 bl2 镜像:

export MILKV_BOARD=milkv-duo
source milkv/boardconfig-milkv-duo.sh

source build/milkvsetup.sh
defconfig cv1800b_milkv_duo_sd

build_fsbl

然后编写一个简单的程序,使用 uart8250 来打印一句字符串:

#define UART0_THR 0x04140000
#define UART0_LSR 0x04140014

	.section .text
	.global _start
_start:
	/* BL33 information */
	j real_start
	.balign 4
	.word 0x33334c42  /* b'BL33' */
	.word 0xdeadbeea  /* CKSUM */
	.word 0xdeadbeeb  /* SIZE */
	.quad 0x80200000  /* RUNADDR */
	.word 0xdeadbeec
	.balign 4
	j real_start
	.balign 4
	/* Information end */

real_start:
	la s0, str
1:
	lbu a0, (s0)
	beqz a0, exit
	jal ra, uart_send
	addi s0, s0, 1
	j 1b

exit:
	j exit

uart_send:
	/* Wait for tx idle */
	li t0, UART0_LSR
	lw t1, (t0)
	andi t1, t1, 0x20
	beqz t1, uart_send
	/* Send a char */
	li t0, UART0_THR
	sw a0, (t0)
	ret

.section .rodata
str: 
	.asciz "Hello Milkv-duo!\n"

编译:

riscv64-unknown-elf-gcc -nostdlib -fno-builtin -march=rv64gc -mabi=lp64f -g -Wall -Ttext=0x80200000 -o bl33.elf start.S

riscv64-unknown-elf-objcopy -O binary bl33.elf bl33.bin

此时可以用 hd 看一下生成的 bin 文件,会发现已经存在了用于校验的两行数据,而 opensbi 实际会跳转到 0x80200020 来执行我们的程序。

00000000  05 a0 01 00 42 4c 33 33  ea be ad de eb be ad de  |....BL33........|
00000010  00 00 20 80 00 00 00 00  ec be ad de 11 a0 01 00  |.. .............|

进入到 fsbl 目录生成 fip.bin 文件:

cd fsbl/

./plat/cv180x/fiptool.py -v genfip \
    'build/cv1800b_milkv_duo_sd/fip.bin' \
    --MONITOR_RUNADDR="0x0000000080000000" \
    --CHIP_CONF='build/cv1800b_milkv_duo_sd/chip_conf.bin' \
    --NOR_INFO='FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' \
    --NAND_INFO='00000000'\
    --BL2='build/cv1800b_milkv_duo_sd/bl2.bin' \
    --DDR_PARAM='test/cv181x/ddr_param.bin' \
    --MONITOR='../opensbi/build/platform/generic/firmware/fw_dynamic.bin' \
    --LOADER_2ND='/path/to/bl33.bin' \
    --compress='lzma'

最后将 fip.bin 文件放到 tf 卡,使用 128000 比特率连接到板子的串口,就能看到我们的打印信息了:

image

6 Likes

请问我想知道的是,按照标准制作一个fip.bin然后直接开始加载自己的内核会有什么要求,是不是只需要满足fip文件的格式就行?

1 Like

对的,用 sbi 引导的话只要添加 bl33 校验信息就可以了

1 Like

我更想了解的是如果我不想使用opensbi呢,我希望自己初始化设备。自制的fip直接加载自己的loader会有什么要求吗

1 Like

我想我理解了,我可以直接在bl33中加载自己的任何东西,然后直接跳转,bl33之后都可以由我自由发挥。

对的,bl2 阶段官方好像没有开源,没办法进行修改,目前来看 opensbi 应该是必须要的。

Hey, guys
I made a youtube video showing those steps
https://youtu.be/UrKmSipiXpc?si=H8WHHjq7kc-XSJHM

5 Likes

Very nice info! Just to add further to this, I have found a way to skip and bypass the OpenSBI completely, allowing to boot directly into your own program. This puts your program in highest privilege M-mode (machine mode), and is perfect for using the Milk V Duo/256M like an embedded MCU.

Basically, there is an empty jump function inside the FSBL source code - duosdk/fsbl/lib/cpu/riscv/bl2_helper.c. We can complete it by modifying to this:

void jump_to_loader_2nd(uintptr_t loader_2nd_entry)
{
	void (*entry)() = (void *)loader_2nd_entry;
	entry();
}

Next, follow the same procedure to build the fsbl as well as your baremetal program as described by Judehahh, but run this instead:

cd fsbl

./plat/cv180x/fiptool.py -v genfip \
    'build/sg2002_milkv_duo256m_musl_riscv64_sd/fip.bin' \
    --MONITOR_RUNADDR="0x0" \
    --CHIP_CONF='build/sg2002_milkv_duo256m_musl_riscv64_sd/chip_conf.bin' \
    --NOR_INFO='FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' \
    --NAND_INFO='00000000'\
    --BL2='build/sg2002_milkv_duo256m_musl_riscv64_sd/bl2.bin' \
    --LOADER_2ND='/path/to/bl33.bin'

Btw, since I am using the latest SDK v2 I had to remove DDR_PARAM=‘test/cv181x/ddr_param.bin’. By setting the MONITOR_RUNADDR=0 we bypass the loading of OpenSBI. Also we must now disable compression because that is an option supported by OpenSBI but we are not using that now.

Your serial output should look like this:

3 Likes