riscv-5-设备树
1.设备树介绍
参考:
设备树:是一种描述硬件的数据结构,提供一种语言将硬件配置从Linux内核源码中提取出来,使得目标板和设备变成数据驱动的,它们必须基于传递给内核的数据进行初始化。
设备树文件 使用一种”Device Tree Source”(DTS)
的语言编写,文件描述了硬件设备的层次结构,寄存器地址,中断线路,DMA通道和其他的相关属性。
设备树文件 经过DTC
编译后会生成一种称为”Device Tree Blob”(DTB)的二进制格式。DTB文件在引导过程中由引导加载程序(Bootloader)提供给内核。内核会解析DTB文件,根据其中的描述信息初始化硬件设备,并加载相应的驱动程序。
名词解释:
DTS
device tree source ,.dts文件,是一种ASCII 文本格式的文件,一般一个文件对应一个硬件平台DTC
:device tree complier 编译设备树源码的编译工具,一般情况下需要手动安装这个编译工具DTB
:device tree bin 生成的编译文件
2.编写quard_star的设备树文件
新建dts文件夹和quard_star_sbi.dts
dts/quard_star_sbi.dts
1 | #address-cells = <0x2>; |
- compatible属性:属性值类型:字符串,
- compatible是系统用来决定绑定到设备的设备驱动的关键。 compatible属性是用来查找节点的方法之一,另外还可以通过节点名或节点路径查找指定节点。
- model属性:属性值类型:字符串
- model属性用于指定设备的制造商和型号,推荐使用“制造商, 型号”的格式,当然也可以自定义。
- reg属性
- reg属性描述设备资源在其父总线定义的地址空间内的地址。通常情况下用于表示一块内存的起始地址(偏移地址)和长度,
- chosen子节点:chosen子节点位于根节点下,
- chosen子节点不代表实际硬件,它主要用于给内核传递参数。 这里设置了uart0
总的代码,定义了CPU uart 中断等
1 | /dts-v1/; |
3.start.s重新编写
start.s需要重新编写用于引导加载opensbi固件以及设备树,然后跳转到DRAM执行opensbi
boot/start.s
1 | .macro loop,cunt |
解析:
1 | .macro loop,cunt |
loop
宏,这里定义了一个双重循环,用于执行一个固定次数的空操作。宏接收一个参数cunt
表示外层循环数 t1是内存循环数
1 | .macro load_data,_src_start,_dst_start,_dst_end |
load_data
宏,定义了一个从源地址加载数据到目标地址的循环,直到目标地址到达结束地址。
1 | .section .text |
.section .text
:定义代码段(.text 段),用于存放可执行代码。.globl _start
:声明_start
标签为全局符号,使其在链接时可见。.type _start, @function
:指定_start
为函数类型
1 | //load opensbi_fw.bin |
- 加载opensbi固件
- load_data a0, a1, a2: 调用宏
load_data
,将从0x20200000
到0x20400000
的数据复制到0x80000000
到0x80200000
。
1 | //load qemu_sbi.dtb |
- 加载qemu sbi 设备树
- load_data a0, a1, a2: 调用宏
load_data
,将从0x20080000
到0x20100000
的数据复制到0x82200000
到0x82280000
。
1 | csrr a0, mhartid |
检查当前hart,
csrr a0, mhartid: 读取当前 hart ID(硬件线程 ID)到寄存器
a0
。li t0, 0x0: 将
0
加载到寄存器t0
。beq a0, t0, _no_wait: 如果
a0
等于t0
(即当前 hart ID 为 0),跳转到_no_wait
标签。loop 0x1000: 否则,执行一个循环(定义在上面的
loop
宏中),大约等待一段时间。nowait
li a1, 0x822: 将
0x822
加载到寄存器a1
中。slli a1, a1, 20: 将
a1
左移 20 位,使其值变为0x82200000
。 —设备树li t0, 0x800: 将
0x800
加载到寄存器t0
中。slli t0, t0, 20: 将
t0
左移 20 位,使其值变为0x80000000
。 —opensbijr t0: 跳转到
t0
(即0x80000000
)处,开始执行新程序。 —opensbi
总结,这段代码实现了以下功能:
- 将 OpenSBI 固件从
0x20200000
复制到0x80000000
。 - 将 QEMU SBI 设备树从
0x20080000
复制到0x82200000
。 - 检查当前 hart ID,如果不是 0,则执行一个等待循环。
- 跳转到
0x80000000
处,执行新的程序。
4.build.sh和 run.sh修改
build.sh
1 | ---------------------------编译opensbi----------------------------- |
操作:
- 首先编译了opensbi,并且指定了板
- 编译生成设备树dtb
- 合成固件
- 根据dd命令的使用可以看见将
quard_star_sbi.dtb
写入到了偏移地址为0x80000
的地方,用于这个固件会被加载到flash
处,所以设备树的地址为:0x20080000
—- 对应了start.s
文件加载设备树的地址 fw_jump.bin
地址偏移量为2K*1K= 0x2000000
,因此fw_jump.bin
的地址偏移量为0x2000000
,同理 这个固件会被加载到flash
,所以opensbi
固件的地址为:0x20200000
—对应start.s
加载opensbi
的位置lowlevel_fw.bin
偏移量地址为 0,因此lowlevel_fw.bin
的地址为:0x20000000
dd of=fw.bin bs=1k count=32k if=/dev/zero
来填充填充 32K的0,这是qemu
使用-drive
加载的固件的大小要大于等于定义的flash
的大小,不然会报错。
注意
现在固件包含三个内容:
- 首先是起始
0x20000000
的lowlevel_fw.bin
- lowlevel的作用是将opensbi 以及设备树文件从flash中 加载到 DRAM(0x80000000),然后跳转到DRAM执行
- 然后是
0x20080000
地址处的dtb
文件 - 接下来是
0x20200000
处的opensbi
程序
run.sh
1 | SHELL_FOLDER=$(cd "$(dirname "$0")";pwd) |
这里添加了一个qemu.log
会生成汇编代码,可以根据这个log来查看固件是否有正确的加载、跳转。
执行build.sh run.sh
报错./build.sh: 45: dtc: not found
执行:sudo apt-get install device-tree-complier
还需要修改
quard_star.c 把DRAM内存扩大
1 | [QUARD_STAR_DRAM] = { 0x80000000, 0x40000000 }, |
显示结果
5.内存布局
rom
中先从(fw_dynamic_info
这个结构体指示了flash
的起始地址 在哪找到它)flash
记载lowlevel
,然后跳转执行flash
(flash
中的固件是直接drive
注入的)flash
中存放lowerlevel.bin
,opensbi
固件,设备树固件- 将
opensbi
,设备树固件加载到DRAM
并跳转执行