海思hi3520dv400 kernel分析(3)——设备树支持
概念:
- FDT:Flattened Device Tree (扁平设备树)是一种数据结构,用来描述设备的硬件配置信息,它源自开放固件使用的设备树格式。
- DTS:Device tree source(设备树源)是一个文本文件,以人类可读的形式描述了计算机系统的硬件资源
- DTB:Device tree blob 它是由DTS文本文件编译生成的二进制文件
设备树的引入是为了解决linux kernel越来越臃肿的问题
设备树特点:
在设备树dts文件指定硬件资源,dts被编译为dtb文件, 在启动单板时,U-boot会将dtb文件传给内核,使得驱动程序与硬件分离,我们只需要修改dts文件,便能实现需求。这就是设备树易于扩展,硬件有变动时不需要重新编译内核或驱动程序,只需要提供不一样的dtb文件。
而对于传统字符驱动的编写有两种方式:
- 一是在驱动程序中,直接写死硬件资源,如:GPIO、寄存器地址、中断号等,使得硬件改动时,必须修改驱动程序。
- 二是采用总线驱动platform模型,将硬件资源与驱动软件分离,在platform_device中描述硬件资源,arch/arm/mach-xxx对应的文件,便是以platform_device描述各自CPU对应的硬件资源;在platform_driver中分配/设置/注册 file_operations结构体, 并从platform_device获得硬件资源。这种编写方式使得驱动易于扩展,硬件改动时只需修改platform_device或者platform_driver,这就导致linux内核产生大量的冗余代码。
要将FDT信息传递给kernel有两种方式:FDT兼容TAG模式和FDT取代TAG模式
(一)FDT兼容TAG
(1)内核配置
使能CONFIG_ARM_APPENDED_DTB功能,该功能是将DTB文件拼接到uImage的后面
500 #501 # Boot options502 #503 CONFIG_USE_OF=y504 CONFIG_ATAGS=y505 # CONFIG_DEPRECATED_PARAM_STRUCT is not set506 # CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set 507 CONFIG_ZBOOT_ROM_TEXT=0508 CONFIG_ZBOOT_ROM_BSS=0509 CONFIG_ARM_APPENDED_DTB=y510 CONFIG_ARM_ATAG_DTB_COMPAT=y511 CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y512 # CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set513 CONFIG_CMDLINE=""514 # CONFIG_KEXEC is not set515 # CONFIG_CRASH_DUMP is not set516 CONFIG_AUTO_ZRELADDR=y
(2)构建zImage-dtb
#arch/arm/boot/Makefile68 $(obj)/zImage-dtb: $(obj)/zImage $(DTB_OBJS) FORCE69 $(call if_changed,cat)70 @echo ' Kernel: $@ is ready'
实际执行的命令是:
cmd_arch/arm/boot/zImage-dtb := (cat arch/arm/boot/zImage arch/arm/boot/dts/hi3520dv400-demb.dtb > arch/arm/boot/zImage-dtb) ||(rm -f arch/arm/boot/zImage-dtb; false)
将arch/arm/boot/dts/hi3520dv400-demb.dtb拼接到arch/arm/boot/zImage的后面生成arch/arm/boot/zImage-dtb文件。
(3)参数转换
在内核自解压阶段,自解压程序会去判断是否有设备的存在(通过设备树魔术判断),然后在再读取设备树大小等信息。然后将设备树指针保存到r2寄存器中去。
#linux/arch/arm/boot/compressed/head.S /*设备树相关的操作*/mov r5, #0 @ init dtb size to 0#ifdef CONFIG_ARM_APPENDED_DTB
/** r0 = delta* r2 = BSS start* r3 = BSS end* r4 = final kernel address (possibly with LSB set)* r5 = appended dtb size (still unknown)* r6 = _edata* r7 = architecture ID* r8 = atags/device tree pointer* r9 = size of decompressed image* r10 = end of this image, including bss/stack/malloc space if non XIP* r11 = GOT start* r12 = GOT end* sp = stack pointer** if there are device trees (dtb) appended to zImage, advance r10 so that the* dtb data will get relocated along with the kernel if necessary.*/ldr lr, [r6, #0]
#ifndef __ARMEB__ldr r1, =0xedfe0dd0 @ sig is 0xd00dfeed big endian
#elseldr r1, =0xd00dfeed /**海思使用的是小端模式**/
#endifcmp lr, r1 /* 通过魔术字来判断是否内核镜像后有 DTB */bne dtb_check_done @ not found#ifdef CONFIG_ARM_ATAG_DTB_COMPAT/**如果确实在zImage上附加了DTB,并且确实有ATAG列表,*我们希望将后者翻译成此处的前者。 *为了安全起见,让我们暂时将堆栈移到malloc区域。 *尚未发生GOT修正,但我们要调用的代码均未使用任何全局变量。*/add sp, sp, #0x10000stmfd sp!, {r0-r3, ip, lr}mov r0, r8mov r1, r6sub r2, sp, r6bl atags_to_fdt/** If returned value is 1, there is no ATAG at the location* pointed by r8. Try the typical 0x100 offset from start* of RAM and hope for the best.*/cmp r0, #1sub r0, r4, #TEXT_OFFSETbic r0, r0, #1add r0, r0, #0x100mov r1, r6sub r2, sp, r6bleq atags_to_fdtldmfd sp!, {r0-r3, ip, lr}sub sp, sp, #0x10000
#endif/* r6 指向的是设备树的地址 也是 _edata* 此时 r8 指向了设备树,* 在下面跳转至解压后的内存执行时* mov r2, r8 @ restore atags pointer* 从而 r8 指向的设备树会直接传递进内核*/mov r8, r6 @ use the appended device tree/** Make sure that the DTB doesn't end up in the final* kernel's .bss area. To do so, we adjust the decompressed* kernel size to compensate if that .bss size is larger* than the relocated code.*//**调整解压后的kernel大小*如果 _kernel_bss_size - (dtb/_edata - wont_overwrite) 大于0*则 size of decompressed image 会增加上面的值*从而使得下面 relocate 过程里, 目的地址在解压后内核的最后,*再往后移 _kernel_bss_size - (dtb/_edata - wont_overwrite)*从而使得 relocate 的代码与 kernel 的 bss有重合,但是 dtb 没有。**如果 _kernel_bss_size - (dtb/_edata - wont_overwrite) 小于0,*则 relocate 的代码段(dtb/_edata - wont_overwrite)全部覆盖 kernel bss段后,*还会占用后面的一部分,而 dtb 还在这后面,肯定不会与 kernel bss 有重合了。**在该代码段执行完后,relocate的代码段内存空间可以在内核启动后覆盖 即被 bss使用,*但是 dtb 对应的空间则不要一直保留,不能被bss初始化为全0. */ldr r5, =_kernel_bss_sizeadr r1, wont_overwritesub r1, r6, r1subs r1, r5, r1addhi r9, r9, r1/**dtb 开始4字节是魔术,接下来4字节是dtb文件大小**/ldr r5, [r6, #4]
#ifndef __ARMEB__ /**ARM 设备不会进入这里**//* convert r5 (dtb size) to little endian */eor r1, r5, r5, ror #16bic r1, r1, #0x00ff0000mov r5, r5, ror #8eor r5, r5, r1, lsr #8
#endif/*dtb 大小设置8字节对齐*//* preserve 64-bit alignment */add r5, r5, #7bic r5, r5, #7/* r6 = _edata * r5 表示设备树的大小* 使得 _edata 包含了 设备树的大小,* 在下面 Relocate时,同时也会将设备树 Relocate*//* relocate some pointers past the appended dtb */add r6, r6, r5add r10, r10, r5/* 栈地址也扩大 */add sp, sp, r5
dtb_check_done:
#endif /*end of CONFIG_ARM_APPENDED_DTB*/
这里需要注意一个atags_to_fdt 函数,该函数的功能是,当uboot有传递参数给kernel,同时uImage找那个也包含了设备树时,这个函数会将uboot的atags参数转换为fdt格式。
/*/arch/arm/kernel/atags_to_fdt.c*/
/** Convert and fold provided ATAGs into the provided FDT.** REturn values:* = 0 -> pretend success* = 1 -> bad ATAG (may retry with another possible ATAG pointer)* < 0 -> error from libfdt*/
int atags_to_fdt(void *atag_list, void *fdt, int total_space)
{struct tag *atag = atag_list;/* In the case of 64 bits memory size, need to reserve 2 cells for* address and size for each bank */uint32_t mem_reg_property[2 * 2 * NR_BANKS];int memcount = 0;int ret, memsize;/* make sure we've got an aligned pointer */if ((u32)atag_list & 0x3)return 1;/* if we get a DTB here we're done already */if (*(u32 *)atag_list == fdt32_to_cpu(FDT_MAGIC))return 0;/* validate the ATAG */if (atag->hdr.tag != ATAG_CORE ||(atag->hdr.size != tag_size(tag_core) &&atag->hdr.size != 2))return 1;/* let's give it all the room it could need */ret = fdt_open_into(fdt, fdt, total_space);if (ret < 0)return ret;for_each_tag(atag, atag_list) {if (atag->hdr.tag == ATAG_CMDLINE) {/* Append the ATAGS command line to the device tree* command line.* NB: This means that if the same parameter is set in* the device tree and in the tags, the one from the* tags will be chosen.*/if (do_extend_cmdline)merge_fdt_bootargs(fdt,atag->u.cmdline.cmdline);elsesetprop_string(fdt, "/chosen", "bootargs",atag->u.cmdline.cmdline);} else if (atag->hdr.tag == ATAG_MEM) {if (memcount >= sizeof(mem_reg_property)/4)continue;if (!atag->u.mem.size)continue;memsize = get_cell_size(fdt);if (memsize == 2) {/* if memsize is 2, that means that* each data needs 2 cells of 32 bits,* so the data are 64 bits */uint64_t *mem_reg_prop64 =(uint64_t *)mem_reg_property;mem_reg_prop64[memcount++] =cpu_to_fdt64(atag->u.mem.start);mem_reg_prop64[memcount++] =cpu_to_fdt64(atag->u.mem.size);} else {mem_reg_property[memcount++] =cpu_to_fdt32(atag->u.mem.start);mem_reg_property[memcount++] =cpu_to_fdt32(atag->u.mem.size);}} else if (atag->hdr.tag == ATAG_INITRD2) {uint32_t initrd_start, initrd_size;initrd_start = atag->u.initrd.start;initrd_size = atag->u.initrd.size;setprop_cell(fdt, "/chosen", "linux,initrd-start",initrd_start);setprop_cell(fdt, "/chosen", "linux,initrd-end",initrd_start + initrd_size);}}if (memcount) {setprop(fdt, "/memory", "reg", mem_reg_property,4 * memcount * memsize);}return fdt_pack(fdt);
}
(4)内核获取DTB文件地址:
1、bootloader启动内核时,会设置r0,r1,r2三个寄存器:
r0一般设置为0;
r1一般设置为machine id (在使用设备树时该参数没有被使用);
r2一般设置ATAGS或DTB的开始地址
2、通过head.S head-common.S处理,获得dtb文件指针__atags_pointer
bl __lookup_processor_type //使用汇编指令读取CPU ID, 根据该ID找到对应的proc_info_list结构体(里面含有这类 CPU 的初始化函数、信息)
bl __vet_atags //判断是否存在可用的ATAGS或DTB
bl __create_page_tables //创建页表, 即创建虚拟地址和物理地址的映射关系
b __enable_mmu //使能MMU, 以后就要使用虚拟地址了
ldr r13, =__mmap_switched //上述函数里将会调用__mmap_switched
3、//r9 = processor ID
__mmap_switched:
//缓存 r1 r2
mov r7, r1
mov r8, r2__mmap_switched_data:
.long processor_id @ r0
.long __machine_arch_type @ r1
.long __atags_pointer @ r2adr r4, __mmap_switched_data //将存储变量的地址赋给r4
4、//将u-boot传递给内核的参数r0 r1 r2 分别赋给C变量 processor_id、__machine_arch_type、__atags_pointer
ldmia r4, {r0, r1, r2, r3}
str r9, [r0] @ Save processor ID
str r7, [r1] @ Save machine type
str r8, [r2] @ Save atags pointer
(5)内核解析dtb文件匹配单板
内核运行起来之后在
start_kernel ==> setup_arch(&command_line); ==>setup_arch==>
setup_processor();
mdesc = setup_machine_fdt(__atags_pointer);
if (!mdesc)
mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
之后就是设备树的一些应用
(二)FDT取代TAG
- 在海思设备上默认并不支持该功能,并且因为海思自己在uboot上做的修改比较大,需要同时修改uboot与kernel才能正常启动,如果以后有机会再进行分析。
- 它的基本原理应该是将bootargs中的参数放置到设备树中,在通过寄存器r2将设备树的地址传递给kernel,kernel起来之后通过该地址来解析设备信息。
参考内容:
设备树(三)—— linux内核对设备树的支持
https://blog.csdn.net/ggxyx123/article/details/85595173
linux系统之驱动与FDT
https://blog.csdn.net/eleven_xiy/article/details/72835181
Linux内核DTB文件启动的几种方式
https://www.cnblogs.com/iot-yun/p/11403498.html
----------------------------------------------------------------2022.08.28----------------------------------------------------------------
|公|_新的文章内容和附件工程文件
|众|_已更新在博客首页和:
|号|:liwen01
海思hi3520dv400 kernel分析(3)——设备树支持相关推荐
- 海思Hi3516智能分析引擎应用介绍
安防监控正在步入高清化.智能化时代,海思推出的Hi3516正是针对高清IPCamera的一款专业高端SOC芯片,除了1080p@30fpsH.264多码流编码.优异的ISP和编码视频质量优势外,其高性 ...
- 海思OMX代码分析---技术片段
一.引言: 海思stb产品omx框架中使用的一些不错的代码技术,这个博客分析这些代码片段. 二.具体分析: 1.字符串衔接: 海思在通过组件名加载动态库的时候,将组件名与前后缀衔接使用的是strnca ...
- Linux rtsp客户端 海思,海思rtsp软件分析
rtsp 概念啥的就不介绍了,记不住. 简单分析下. 海思板子是服务器,电脑为客服端. 首先 socket setsockopt bind listen (注意:po ...
- 海思HI3518EV300作为USB视频设备的相关配置
作为USB设备,由于需求上没有要求设备需要播放声音,所以把扬声器去掉了,但是在开发过程中遇到设备插到电脑上,电脑将板子的扬声器作为了默认音频输出设备,而板子又没有扬声器,所以电脑识别扬声器失败,出现下 ...
- 海思Hi3520DV400 uboot看门狗喂狗
看门狗的功能这里不再介绍,自行百度谷歌搜索.这里记录一下Hi3520DV400看门狗的喂狗设置.首先查 include/watchdog.h中的代码,可以发现,如果需要启用硬看门狗,需要定义宏:CON ...
- 海思编译kernel
编译: 进入内核目录,执行命令:make ARCH=arm CROSS_COMPILE=arm-himix100-linux- uImage -j8
- 消息称华为麒麟 A2 处理器已有量产能力,海思将率先在可穿戴设备领域回归
华为海思将于 2023 年第 3 季度或第 4 季度推出麒麟 A2 处理器,海思将在可穿戴设备处理器领域率先回归. 报道指出,华为已经测试麒麟 A2 很长一段时间,已准备试产,且具备量产能力.不过,在 ...
- Exynos4412 内核移植(六)—— 设备树解析
一.描述 ARM Device Tree起源于OpenFirmware (OF),在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相 ...
- 海思hi3516dv300 配置uart3
目前uart3发送和接收正常. 通过Hi3516DV300 专业型 Smart IP Camera SoC 用户指南.pdf文档得到hi3516dv300共有5个串口, 板子卖家给的资料中串口没有标注 ...
最新文章
- 无盘服务器2个dhcp,无盘 dhcp 服务器配置
- Java实现斐波那契数列Fibonacci
- 大学计算机在线阅读,大学计算机基础作业与答案.doc
- P4300-[AHOI2006]上学路线【网络流,最短路】
- Linux fast open,Linux内核3.7 TCP Fast Open验证实例
- 信息学奥赛C++语言:枪声问题
- vue create 新项目时,命令行工具卡死(npm卡死)
- 计算机组装与维护时dm是指,《计算机组装与维护》试题答案
- Chainlink预言机正式集成至币安智能链
- mysql 查询之聚合查询
- python读写excel模块pandas_python3 基于pandas读写Excel
- 10个最新优秀手机应用界面设计实例
- 利用反射实现工厂模式
- 赵雄飞uc讲解20110216
- 详解示波器的三个主要参数:采样率,存储深度,带宽
- 海外国外支付渠道接口对接
- proftpd java_Proftpd文件系统安装
- 魔镜连接本地mysql_打造属于你的树莓派“魔镜”—硬件篇
- 认识SlackwareLinux及制作系统安装磁片之关於bootdisk(转)
- git clone https 克隆失败解决办法