一. 前言
这一篇分享OpenSBI的单独构建,Kconfig配置以及使用GDB进行仿真。
为了方便编辑阅读,我们安装VSCODE
Windows主机下安装VSCODE,这里略去过程。
进入ubuntu终端
输入code自动安装vscode
~$ code :
Updating VS Code Server to version b1c0a14de1414fcdaa400695b4db1c0799bc3124
Removing previous installation...
Installing VS Code Server for Linux legacy-x64 (b1c0a14de1414fcdaa400695b4db1c0799bc3124)
Downloading: 100%
Unpacking: 100%
Unpacked 1754 files and folders to /home/qinyunti/.vscode-server/bin/b1c0a14de1414fcdaa400695b4db1c0799bc3124.
安装完后自动打开vscode
二.单独构建OpenSBI
官方仓库
https://github.com/riscv-software-src/opensbi.git
玄铁仓库
https://github.com/c-sky/opensbi.git
我们这里使用玄铁的,因为要保证工具链和仓库相对应,opensbi最新的可能玄铁老的工具链一些特性不支持。
https://github.com/c-sky/opensbi.git
下载代码进入目录
git clone https://github.com/c-sky/opensbi.git
cd opensbi
设置工具链
export CROSS_COMPILE=~/buildroot/thead_9xxf_enhanced_5.10_glibc_br_defconfig/host/bin/riscv64-unknown-linux-gnu-
设置平台位宽
export PLATFORM_RISCV_XLEN=64
make
报错
/usr/bin/env: ‘bash\r’: No such file or directory
修改makefile文件LFCR改为LF
以上只是编译.a文件 ,位于build/lib下
要编译固件则需要配置PLATFORM,并且使用menuconfig进行配置
make PLATFORM=generic menuconfig
报错
/usr/bin/env: ‘python3\r’: No such file or directory
是因为windows下是LFCR,linux下都要用LF,如果在windows下用gitbash下载会自动加LFCR,此时就不能直接在linux下构建。所以避免麻烦,直接在linux下git clone代码。
默认menuconfig后保存为generic/kconfig/.config,Kconfig相关配置下一节介绍。
配置完后构建
make PLATFORM=generic
生成的固件位于
build/platform/generic/firmware/
三.Kconfig配置
通过menuconfig,Kconfig文件可以了解程序的基本组成,即哪些文件会编译。
make PLATFORM=generic menuconfig进行配置
保存的.config位于
build/platform/generic/kconfig/.config
生成的头文件位于
build/platform/generic/kconfig/autoconf.h
Makefile中如下语句会自动包含该头文件
export KCONFIG_AUTOHEADER=$(KCONFIG_DIR)/autoconf.h
ifdef PLATFORM
GENFLAGS += -include $(KCONFIG_AUTOHEADER)
endif
3.1主菜单
先从根目录的Kconfig开始
mainmenu显示的主菜单内容
但是OPENSBI_SRC_DIR,OPENSBI_PLATFORM,OPENSBI_PLATFORM_SRC_DIR
三个字符串配置没有显示,因为string后面是空的。
option env="" 表示选项值来源于””里的环境变量值。
我们修改如下
config OPENSBI_SRC_DIR
string "OPENSBI_SRC_DIR"
option env="OPENSBI_SRC_DIR"
config OPENSBI_PLATFORM
string OPENSBI_PLATFORM
option env="OPENSBI_PLATFORM"
config OPENSBI_PLATFORM_SRC_DIR
string "OPENSBI_PLATFORM_SRC_DIR"
option env="OPENSBI_PLATFORM_SRC_DIR"
Makefile中设置了这几个环境变量
export OPENSBI_SRC_DIR=$(src_dir)
export OPENSBI_PLATFORM=$(PLATFORM)
export OPENSBI_PLATFORM_SRC_DIR=$(platform_src_dir)
其中
src_dir=$(CURDIR)
PLAFROM来源于命令中的PLATFORM=generic
export platform_subdir=$(PLATFORM)
export platform_src_dir=$(platform_parent_dir)/$(platform_subdir)
ifdef PLATFORM_DIR
platform_dir_path=$(shell $(READLINK) -f $(PLATFORM_DIR))
ifdef PLATFORM
platform_parent_dir=$(platform_dir_path)
else
PLATFORM=$(shell basename $(platform_dir_path))
platform_parent_dir=$(shell realpath ${platform_dir_path}/..)
endif
else
platform_parent_dir=$(src_dir)/platform
endif
这里没定义PLATFORM_DIR所以
platform_parent_dir=$(src_dir)/platform
即当前目录下/platform/generic
显示如下
可以指定选项回车,再修改
我们继续看下一项
前面看到OPENSBI_PLATFORM_SRC_DIR为
platform/generic/所以
$(OPENSBI_PLATFORM_SRC_DIR)/Kconfig
对应platform/generic/Kconfig文件
相应的后面两个
$(OPENSBI_SRC_DIR)/lib/sbi/Kconfig
对应
lib/sbi/Kconfig
$(OPENSBI_SRC_DIR)/lib/utils/Kconfig
对应
lib/utils/Kconfig
$(OPENSBI_SRC_DIR)/firmware/Kconfig
对应
firmware/Kconfig
该文件为空,所以无显示
3.2platform/generic/Kconfig
首先来看,以下内容并没有显示,因为bool后面是空
config PLATFORM_GENERIC
bool
select FDT
select FDT_DOMAIN
select FDT_PMU
default y
修改如下
config PLATFORM_GENERIC
bool PLATFORM_GENERIC
select FDT
select FDT_DOMAIN
select FDT_PMU
default y
此时可以看到选项,默认default y是选中的
其中select表示PLATFORM配置y了,自动配置FDT,FDT_DOMIN,FDT_PMU为y
上下箭头移动,选中PLATFORM_GENERIC,按Y选中显示*,按N不选中显示空。
不选中时,后面的内容全无,因为后面都是需要PLATFORM_GENERIC为y的
if PLATFORM_GENERIC
....
endif
如下字符串
config PLATFORM_GENERIC_NAME
string "Platform default name"
default "Generic"
对应如下
以下部分
config PLATFORM_GENERIC_MAJOR_VER
int "Platform major version"
range 0 65535
default 0
config PLATFORM_GENERIC_MINOR_VER
int "Platform minor version"
range 0 65535
default 1
两个版本整数,默认值分别为0,1,范围0~65535
以下部分
config PLATFORM_ALLWINNER_D1
bool "Allwinner D1 support"
depends on FDT_IRQCHIP_PLIC
default n
config PLATFORM_ANDES_AE350
bool "Andes AE350 support"
select SYS_ATCSMU
default n
config PLATFORM_RENESAS_RZFIVE
bool "Renesas RZ/Five support"
select ANDES45_PMA
select ANDES_SBI
default n
config PLATFORM_SIFIVE_FU540
bool "SiFive FU540 support"
default n
config PLATFORM_SIFIVE_FU740
bool "SiFive FU740 support"
depends on FDT_RESET && FDT_I2C
default n
config PLATFORM_STARFIVE_JH7110
bool "StarFive JH7110 support"
default n
对应如下
config PLATFORM_ALLWINNER_D1
bool "Allwinner D1 support"
depends on FDT_IRQCHIP_PLIC
default n
表示FDT_IRQCHIP_PLIC为y则PLATFORM_ALLWINNER_D1为y
config PLATFORM_ANDES_AE350
bool "Andes AE350 support"
select SYS_ATCSMU
default n
表示PLATFORM_ANDES_AE350为y则SYS_ATCSMU为y
config PLATFORM_RENESAS_RZFIVE
bool "Renesas RZ/Five support"
select ANDES45_PMA
select ANDES_SBI
default n
表示PLATFORM_RENESAS_RZFIVE为y则
ANDES45_PMA和ANDES_SBI为y
config PLATFORM_SIFIVE_FU540
bool "SiFive FU540 support"
default n
PLATFORM_SIFIVE_FU540默认为0
config PLATFORM_SIFIVE_FU740
bool "SiFive FU740 support"
depends on FDT_RESET && FDT_I2C
default n
表示
FDT_RESET和FDT_I2C为y,PLATFORM_SIFIVE_FU740才为y
config PLATFORM_STARFIVE_JH7110
bool "StarFive JH7110 support"
default n
PLATFORM_STARFIVE_JH7110默认为0
最后
source "$(OPENSBI_SRC_DIR)/platform/generic/andes/Kconfig"
前面PLATFORM_RENESAS_RZFIVE为y,则ANDES45_PMA和ANDES_SBI为y
所以此时不可选默认选中,
-*- 表示由于其他条件强制选中,不可手动改,[*]表示可以手动改。
config PLATFORM_RENESAS_RZFIVE
bool "Renesas RZ/Five support"
select ANDES45_PMA
select ANDES_SBI
default n
如果要可配,则前面PLATFORM_RENESAS_RZFIVE要取消选中
3.3lib/sbi/Kconfig
几个bool选项,默认都是y
menu "SBI Extension Support"
config SBI_ECALL_TIME
bool "Timer extension"
default y
config SBI_ECALL_RFENCE
bool "RFENCE extension"
default y
config SBI_ECALL_IPI
bool "IPI extension"
default y
config SBI_ECALL_HSM
bool "Hart State Management extension"
default y
config SBI_ECALL_SRST
bool "System Reset extension"
default y
config SBI_ECALL_SUSP
bool "System Suspend extension"
default y
config SBI_ECALL_PMU
bool "Performance Monitoring Unit extension"
default y
config SBI_ECALL_DBCN
bool "Debug Console extension"
default y
config SBI_ECALL_CPPC
bool "CPPC extension"
default y
config SBI_ECALL_LEGACY
bool "SBI v0.1 legacy extensions"
default y
config SBI_ECALL_VENDOR
bool "Platform-defined vendor extensions"
default y
endmenu
3.4lib/utils/Kconfig
又包含每个子文件夹下的kconfig
menu "Utils and Drivers Support"
source "$(OPENSBI_SRC_DIR)/lib/utils/fdt/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/gpio/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/i2c/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/irqchip/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/libfdt/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/regmap/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/sys/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/timer/Kconfig"
endmenu
3.4.1fdt
source "$(OPENSBI_SRC_DIR)/lib/utils/fdt/Kconfig"
中
menuconfig FDT
bool "Flattened Device Tree (FDT) support"
select LIBFDT
default n
在platform/generic/Kconfig中
PLATFORM_GENERIC为y,所以select FDT,FDT也为y
config PLATFORM_GENERIC
bool PLATFORM_GENERIC
select FDT
select FDT_DOMAIN
select FDT_PMU
default y
所以对应-*-如下
菜单内容如下
依赖FDT为y
if FDT
config FDT_DOMAIN
bool "FDT domain support"
default n
config FDT_PMU
bool "FDT performance monitoring unit (PMU) support"
default n
endif
也是前面config PLATFORM_GENERIC时自动选中的,所以也都是-*-
3.4.2Gpio
lib/utils/gpio/Kconfig
menu "GPIO Support"
config FDT_GPIO
bool "FDT based GPIO drivers"
depends on FDT
select GPIO
default n
if FDT_GPIO
config FDT_GPIO_DESIGNWARE
bool "DesignWare GPIO driver"
default n
config FDT_GPIO_SIFIVE
bool "SiFive GPIO FDT driver"
default n
config FDT_GPIO_STARFIVE
bool "StarFive GPIO FDT driver"
default n
endif
config GPIO
bool "GPIO support"
default n
endmenu
FDT_GPIO必须要为y后面的FDT_GPIO_DESIGNWARE,FDT_GPIO_SIFIVE,
FDT_GPIO_STARFIVE才有
FDT_GPIO为y则GPIO为y
FDT_GPIO需要FDT为y
前面看到FDT在config PLATFORM_GENERIC时FDT select为y。
可以不选中这些gpio驱动,实现自己gpio驱动
比如可以自己实现一个FDT_GPIO_XXX驱动
Kconfig添加一个配置config FDT_GPIO_XXX
实现一个fdt_gpio_xxx.c文件
lib/utils/gpio/objects.mk中
添加
carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_XXXX) += fdt_gpio_xxxx
libsbiutils-objs-$(CONFIG_FDT_GPIO_XXXX) += gpio/fdt_gpio_xxxx.o
这里驱动的实现
lib/utils/gpio/objects.mk中,添加到数组中
carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_DESIGNWARE) += fdt_gpio_designware
lib/utils/gpio/fdt_gpio_designware.c中
定义了结构体,数组成员
extern struct fdt_gpio fdt_gpio_designware;
struct fdt_gpio fdt_gpio_designware = {
.match_table = dw_gpio_match,
.xlate = fdt_gpio_simple_xlate,
.init = dw_gpio_init_bank,
};
gpio/fdt_gpio_drivers.carray中
HEADER: sbi_utils/gpio/fdt_gpio.h
TYPE: struct fdt_gpio
NAME: fdt_gpio_drivers
相当于定义了数组
在lib/utils/gpio/fdt_gpio.c中遍历数组
extern struct fdt_gpio *fdt_gpio_drivers[];
static struct fdt_gpio *fdt_gpio_driver(struct gpio_chip *chip)
{
int pos;
if (!chip)
return NULL;
for (pos = 0; pos < fdt_gpio_drivers_size; pos++) {
if (chip->driver == fdt_gpio_drivers[pos])
return fdt_gpio_drivers[pos];
}
return NULL;
}
最终经过以下脚本
compile_carray = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
echo " CARRAY $(subst $(build_dir)/,,$(1))"; \
$(eval CARRAY_VAR_LIST := $(carray-$(subst .c,,$(shell basename $(1)))-y)) \
$(src_dir)/scripts/carray.sh -i $(2) -l "$(CARRAY_VAR_LIST)" > $(1)
调用
scripts/carray.sh
生成c文件
build/platform/generic/lib/utils/gpio/fdt_gpio_drivers.c
自动编译该文件
extern struct fdt_gpio fdt_gpio_designware;
extern struct fdt_gpio fdt_gpio_sifive;
extern struct fdt_gpio fdt_gpio_starfive;
struct fdt_gpio *fdt_gpio_drivers[] = {
&fdt_gpio_designware,
&fdt_gpio_sifive,
&fdt_gpio_starfive,
};
unsigned long fdt_gpio_drivers_size = sizeof(fdt_gpio_drivers) / sizeof(struct fdt_gpio *);
3.4.3I2C/IPI/REGMAP/IRQCHIP/RESET/SERIAL/TIMER
都和GPIO一样
3.4.4SYS
3.4.5Libquad
3.4.6Libfdt
四.Qemu仿真
开一个终端
~/buildroot/thead_9xxf_enhanced_5.10_glibc_br_defconfig/host/csky-qemu/bin/qemu-system-riscv64 -M virt -cpu c910 -bios build/platform/generic/firmware/fw_dynamic.bin -S -s
显示如下
VNC server running on 127.0.0.1:5900
另开一个终端
~/buildroot/thead_9xxf_enhanced_5.10_glibc_br_defconfig/host/bin/riscv64-buildroot-linux-gnu-gdb -f ~/opensbi/build/platform/generic/firmware/fw_payload.elf
target extended-remote localhost:1234
加载程序
load
(gdb) load
Loading section .text, size 0x16428 lma 0x80000000
Loading section .rodata, size 0x2670 lma 0x80017000
Loading section .dynstr, size 0x2bf lma 0x80019670
Loading section .hash, size 0xd0 lma 0x80019930
Loading section .gnu.hash, size 0xf0 lma 0x80019a00
Loading section .dynsym, size 0x318 lma 0x8001a000
Loading section .rela.dyn, size 0x1440 lma 0x8001a318
Loading section .data, size 0xc70 lma 0x80020000
Loading section .dynamic, size 0x110 lma 0x80020c70
Loading section .got, size 0x100 lma 0x80020d80
Loading section .got.plt, size 0x10 lma 0x80020e80
Loading section .htif, size 0x10 lma 0x80020e90
Loading section .payload, size 0x2128 lma 0x80200000
Start address 0x0000000080000000, load size 120375
Transfer rate: 7836 KB/sec, 1770 bytes/write.
(gdb)
在入口处打断点
b *0x80000000
(gdb) b *0x80000000
Breakpoint 1 at 0x80000000: file /home/qinyunti/opensbi/firmware/fw_base.S, line 51.
输入c回车,运行到断点处
c
Continuing.
Breakpoint 1, _start () at /home/qinyunti/opensbi/firmware/fw_base.S:51
51:1129:beg:0x80000000 :
51 MOV_3R s0, a0, s1, a1, s2, a2
单步运行
输入s回车
(gdb) s
/home/qinyunti/opensbi/firmware/fw_base.S:52:1160:beg:0x8000000c
52 call fw_boot_hart
(gdb)
info reg查看寄存器
查看内存
x /10xw 0x80000000
0x80000000 <_start>: 0x00050433 0x000584b3 0x00060933 0x56c000ef
0x80000010 <_start+16>: 0x00050833 0x00040533 0x000485b3 0x00090633
0x80000020 <_start+32>: 0x046358fd 0x1a630118
bt查看函数执行调用
五. 总结
以上通过kconfig文件,了解了Opensbi的源文件组成,同时分享了使用gdb进行调试,其中fdt驱动代码的设计值得参考,即所有驱动都设计通用的数据结构,不同平台实现类似的数据结构即可。而make时自动将上述数据结构写入一个数组中,通过这个数组即可访问不同的驱动实现,这里通过脚本去做这个事情,和uboot中通过链接脚本去查找驱动思想类似,实现有点区别,都值得借鉴。