「设备树」dtb给内核的两种工作模式

article/2025/10/24 6:43:41

一,传递dtb给内核

对于传统bootloader提供两种工作模式:一是启动加载模式(start loading),一是下载模式(downloading)

工作在启动加载模式时,bootloader会自动执行bootcmd命令,

比如:bootcmd=“nand read 0x100000 0x80000000 0x300000; bootm 0x80000000”

uboot首先把内核镜像拷贝到内存地址为0x80000000的地方,然后执行bootm 0x80000000命令。

bootm命令实际上调用的是do_bootm_linux函数:

theKernel (0,bd->bi_arch_number, bd->bi_boot_params);

r0,r1,r2三个寄存器的设置:

r0一般设置为0;

r1一般设置为machine id (在使用设备树时该参数没有被使用);是让内核知道是哪个CPU,从而调用对应的初始化函数

r2一般设置ATAGS或DTB的开始地址;

以前没有使用设备树时,需要bootloader传一个machine id给内核,内核启动的时候会根据这个machine_id来比较内核machine_desc(机器描述结构体)中的.nr,如果相等,就选中了对应的machine_desc(机器描述结构体)),然后调用machine_desc(机器描述结构体)中的.init(初始化函数)。现在使用设备树的话,这个参数就不需要设置了。

对于我们拿到一个新的bootloader,我们怎么能使代码支持dtb模式,我们需要配置#define CONFIG_OF_LIBFDT,可让u-boot支持内核设备树dts,加载命令如下:

bootm <uImage_addr> <initrd_addr> <dtb_addr>
//bootm + uImage地址 + ramdisk地址 + 设备树镜像地址

比如:

//1. 下载内核uImage到内存0x30007FC0
tftp 0x30007FC0 uImage
//2. 下载dtb到内存32000000
tftp 0x30001000 s3c2440-smdk2440.dtb
//3. - 表示不使用ramdisk加载,如果使用ramdisk则提供其加载地址bootm 0x30007FC0 - 0x30001000

内核资料直通车:最新Linux内核源码资料文档+视频资料

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

对于我们下载dtb的地址0x32000000,这个地址有什么要求呢?是随意选择地址就可以,还是要遵循什么原则呢?

不要破坏u-boot本身

不要破坏内核本身: 内核本身的空间不能占用, 内核要用到的内存区域也不能占用

对于该问题,我们拿了一块2440的地址空间分配图来说明该问题

对于dtb的存放,只能存放在空闲区,并且不能与其他区有重合的地方。

2. fdt命令查看设备树

如果修改设备树中的led设备引脚,有两种办法

修改dts文件,重新编译得到dtb并上传烧写

使用uboot提供的一些命令来修改dtb文件,修改后再把它保存到板子上,以后就使用这个修改后的dtb文件移动值,也就是通过memmove处理

对于u-boot提供了fdt的相关命令:

"addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>\n""fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active\n""fdt resize [<extrasize>]            - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed\n""fdt print  <path> [<prop>]          - Recursive print starting at <path>\n""fdt list   <path> [<prop>]          - Print one level starting at <path>\n""fdt get value <var> <path> <prop>   - Get <property> and store in <var>\n""fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>\n""fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>\n""fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>\n""fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]\n""fdt mknode <path> <node>            - Create a new node after <path>\n""fdt rm     <path> [<prop>]          - Delete the node or <property>\n""fdt header                          - Display header info\n""fdt bootcpu <id>                    - Set boot cpuid\n""fdt memory <addr> <size>            - Add/Update memory node\n""fdt rsvmem print                    - Show current mem reserves\n""fdt rsvmem add <addr> <size>        - Add a mem reserve\n""fdt rsvmem delete <index>           - Delete a mem reserves\n""fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree\n""                                        <start>/<end> - initrd start/end addr\n"

实例:

nand read.jffs2 32000000 device_tree      // 从flash读出dtb文件到内存(0x32000000)
fdt addr 32000000                                      // 告诉fdt, dtb文件在哪
fdt print /led pin                                           // 打印/led节点的pin属性
fdt get value XXX /led pin                           // 读取/led节点的pin属性, 并且赋给环境变量XXX
print XXX                                                    // 打印环境变量XXX的值
fdt set /led pin <0x00050005>                     // 设置/led节点的pin属性
fdt print /led pin                                            // 打印/led节点的pin属性
nand erase device_tree                              // 擦除flash分区
nand write.jffs2 32000000 device_tree      // 把修改后的dtb文件写入flash分区

3. u-boot对dtb的支持

dtb可以以两种形式编译到uboot的镜像中

1.dtb和uboot的bin文件分离

现在的uboot已经做得和kernel很像,最主要的一点是,uboot也使用了dtb的方法,将设备树和代码分离开来(当然可以通过宏来控制)。

CONFIG_OF_CONTROL=y
// 用于表示是否使用了dtb的方式CONFIG_OF_SEPARATE=y
// 是否将dtb和uboot分离表一

2.dtb集成到uboot的bin文件内部

3.通过fdtcontroladdr环境变量来指定dtb的地址

4. uboot中如何获取dtb

在uboot初始化过程中,需要对dtb做两个操作:

获取dtb的地址,并且验证dtb的合法性
根据你编译的是集成还是分离,如果是集成的话,需要为dtb预留内存空间并进行relocate
重新获取一次dtb的地址,bootm传递给内核

4.1 获取dtb的地址,并且验证dtb的合法性

在系统起来的时候,进行一串的初始化函数中,fdtdec_setup会对dtb进行合法性验证

static const init_fnc_t init_sequence_f[] = {
...setup_mon_len,
#ifdef CONFIG_OF_CONTROLfdtdec_setup,
#endifreserve_fdt,
...
}

对应代码如下: lib/fdtdec.c:

int fdtdec_setup(void)
{
#if CONFIG_IS_ENABLED(OF_CONTROL)
# ifdef CONFIG_OF_EMBED/* Get a pointer to the FDT */
// 1. 当使用CONFIG_OF_EMBED的方式时,也就是dtb集成到uboot的bin文件中时,通过__dtb_dt_begin符号来获取dtb地址gd->fdt_blob = __dtb_dt_begin;
# elif defined CONFIG_OF_SEPARATE
#  ifdef CONFIG_SPL_BUILD/* FDT is at end of BSS unless it is in a different memory region */if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS))gd->fdt_blob = (ulong *)&_image_binary_end;elsegd->fdt_blob = (ulong *)&__bss_end;#  elif defined CONFIG_FIT_EMBEDgd->fdt_blob = locate_dtb_in_fit(&_end);if (gd->fdt_blob == NULL || gd->fdt_blob <= ((void *)&_end)) {puts("Failed to find proper dtb in embedded FIT Image\n");return -1;}#  else/* FDT is at end of image */
//2. 
//当使用CONFIG_OF_SEPARATE的方式时,也就是dtb追加到uboot的bin文件后面时,通过_end符号来获取dtb地址gd->fdt_blob = (ulong *)&_end;
#  endif
# elif defined(CONFIG_OF_BOARD)/* Allow the board to override the fdt address. */gd->fdt_blob = board_fdt_blob_setup();
# elif defined(CONFIG_OF_HOSTFILE)if (sandbox_read_fdt_from_file()) {puts("Failed to read control FDT\n");return -1;}
# endif
//3. 可以通过环境变量fdtcontroladdr来指定gd->fdt_blob,也就是指定fdt的地址
# ifndef CONFIG_SPL_BUILD/* Allow the early environment to override the fdt address */gd->fdt_blob = (void *)env_get_ulong("fdtcontroladdr", 16,(uintptr_t)gd->fdt_blob);
# endif
#endifreturn fdtdec_prepare_fdt();
}

该函数主要做了以下几件事情:

对于集成的dtb的u-boot,使用__dtb_dt_begin符号来获取dtb地址,如果是分离式的,通过_end符号来获取dtb地址,同时也支持通过环境参数fdtcontroladdr来配置

然后通过fdtdec_prepare_fdt来对fdt进行合法性检查,判断dtb是否存在,以及是否有四个字节对齐。然后再调用fdt_check_header看看头部是否正常,fdt_check_header主要是检查dtb的magic是否正确

int fdtdec_prepare_fdt(void)
{if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) ||fdt_check_header(gd->fdt_blob)) {
#ifdef CONFIG_SPL_BUILDputs("Missing DTB\n");
#elseputs("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n");
# ifdef DEBUGif (gd->fdt_blob) {printf("fdt_blob=%p\n", gd->fdt_blob);print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4,32, 0);}
# endif
#endifreturn -1;}return 0;
}

4.2 为dtb分配新的内存地址空间

当使用CONFIG_OF_EMBED方式时,也就是dtb集成在uboot中的时候,relocate uboot过程中也会把dtb一起relocate,所以这里就不需要处理。当为分离式要为该dtb在内存中分配一片空间即可

static int reserve_fdt(void)
{
#ifndef CONFIG_OF_EMBED/** If the device tree is sitting immediately above our image then we* must relocate it. If it is embedded in the data section, then it* will be relocated with other data.*/if (gd->fdt_blob) {gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);gd->start_addr_sp -= gd->fdt_size;gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);debug("Reserving %lu Bytes for FDT at: %08lx\n",gd->fdt_size, gd->start_addr_sp);}
#endifreturn 0;
}static int reloc_fdt(void)
{
#ifndef CONFIG_OF_EMBEDif (gd->flags & GD_FLG_SKIP_RELOC)return 0;if (gd->new_fdt) {memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size);gd->fdt_blob = gd->new_fdt;}
#endifreturn 0;
}

http://chatgpt.dhexx.cn/article/8XcXmHjV.shtml

相关文章

设备树_dtb文件分析

前言&#xff1a;我之前的原计划是没有打算写设备树dtb文件分析&#xff0c;但是情势所迫啊&#xff01;&#xff0c;学习还是要一步一步来的。 在前面的章节提到过.dts文件以文本方式对系统设备树进行描述&#xff0c;经过Device Tree Compiler(dtc)将dts文件转换成二进制文件…

Linux设备树学习2 - DTB文件格式

一. DTB文件简介 DTB文件是由DTS文件通过dtc命令编译生成的二进制文件。DTS文件不能直接被内核解析&#xff0c;需要编译成DTB文件才可以直接被内核识别并解析使用的。 二. DTB文件内容布局 从上图可以看出&#xff0c;DTB由四个部分组成&#xff0c;分别是struct fdt_header&a…

setUserVisibleHint-- fragment真正的onResume和onPause方法

这个情况仅适合与多个fragment之间切换时统计&#xff0c;而非activity和fragment同时交互&#xff0c;因当时项目为首页4个fargment时长统计&#xff0c;因此适合&#xff0c;经下面网友评论指出&#xff0c;特在这里写出此问题&#xff0c;因最近项目较忙&#xff0c;具体情况…

onCreate与onStart区别,onStart与onResume区别

http://www.cnblogs.com/kofi1122/archive/2011/04/10/2011772.html Activity生命周期之我见 关于Activity生命周期的文章很多&#xff0c;而且大部分也说得很详细&#xff0c;所以作为关于这方面的内容我本来不想多说&#xff0c;但是大家可能跟我之前一样&#xff0c;在看这方…

Flutter BaseWidget 实现onResume、onPause()

熟读唐诗三百首&#xff0c;不会作诗也会吟。——孙洙 最近用Flutter开发的项目算是完成了开发到上线第一阶段了。任何一个项目开始了&#xff0c;若想追求的是更好&#xff0c;那么就需要下功夫对项目用户体验和代码效率深入的研究了。作为用户和产品经理、老板、UI、不懂技术…

Activity生命周期中onStart()和onResume()的区别

Activity生命周期中onStart()和onResume()的区别 在讲onStart()和onResume()的区别之前&#xff0c;必须清楚Activity的四种状态&#xff1a; 1.Running状态&#xff1a;一个新的Activity的启动入栈后&#xff0c;它在屏幕最前端&#xff0c;处于栈的最顶端&#xff0c;此时它…

android Activity的onPause()与onResume()

官方文档地址&#xff1a;http://www.android-doc.com/training/basics/activity-lifecycle/pausing.html#Resume Pause和Resume一个Activity 在一般的app使用中&#xff0c;前台的activity一般是会被视觉组件所遮住的&#xff0c;这就会导致activity的pause。举个例子&#x…

android中onResume、onPause等方法

程序正常启动&#xff1a;onCreate()->onStart()->onResume(); 正常退出&#xff1a;onPause()->onStop()->onDestory() 程序按back 退出&#xff1a; onPause()->onStop()->onDestory(),再进入&#xff1a;onCreate()->onStart()->onResume(); 程序按…

OnStart()方法和OnResume()方法的区别

文章目录 一、Activity的生命周期简述在讲onStart()和onResume()的区别之前&#xff0c;必须清楚Activity的四种状态&#xff1a; 二、OnStart()和OnResume()方法的区别区别&#xff1a; 总结 一、Activity的生命周期简述 在讲onStart()和onResume()的区别之前&#xff0c;必须…

简单介绍几种Java后台开发常用框架组合

01 前言 Java框架一直以来都是面试必备的知识点&#xff0c;而掌握Java框架&#xff0c;不管在成熟的大公司&#xff0c;快速发展的公司&#xff0c;还是创业阶段的公司&#xff0c;都能对当前正在开发中的系统有整体的认知&#xff0c;从而更好的熟悉和学习技术&#xff0c;这…

用IDEA进行Java后台开发(一)

想尝试着进行java后台开发&#xff0c;发现能找到的完整工程教程很多都是用eclipse开发的&#xff0c;于是照着搭了一套开发环境&#xff0c;也简单的写了一个测试代码&#xff0c;由于好几年没有使用eclipse了&#xff0c;快捷键实在用的费劲&#xff0c;遂找到了IDEA这个开发…

Java后台学习路线

2021.01.31.更新 2018.11.10更新&#xff0c;请看文章末尾惊喜 或者点击 https://blog.csdn.net/qq_40374604/article/details/83932163 本文摘自---https://blog.csdn.net/u014624241/article/details/52946378---感谢作者的分享 ①一段摘自知乎的文档 Java就业方向目前主…

Java后台开发一:环境搭建

很久之前就想做Java后台开发了&#xff0c;苦于一直没有找到合适的后台开发教程&#xff0c;某日发现一篇很不错的Java后台开发配置文章&#xff0c;记录如下&#xff1a; jdkeclipsesvnmavenmysqltomcat7.0sublime安装包和jar插件 配置管理工具-SVN http://download.csdn.net/…

Java后台开发知识一览

Java后台开发知识一览 1、后端 WEB服务器&#xff1a;Weblogic、Tomcat、WebSphere、JBoss、Jetty 核心框架&#xff1a;Spring Framework 分布式服务框架 Dubbo&#xff08;感谢浅浅浅丿忧伤指正&#xff09; 安全框架&#xff1a;Apache Shiro 视图框架&#xff1a;Spr…

Java后台开发常见官方网站汇总

Java后台开发常见官方网站汇总 写在前面开发工具篇JDKJavaSE开发文档Oracle数据库&#xff08;收费&#xff09;MySQL数据库&#xff08;免费&#xff09;RedisMongoDBJDBCDruid连接池ElasticsearchKibana——Elasticsearch的客户端RabbitMQMavenGitSpring Framework&#xff0…

【Java后台开发规范】--- 日志的输出

文章目录 前言其他类型的规范 日志输出过多日志重复打印批量打印日志中带方法讲故事日志跟踪其他基本要求 前言 做Java开发的&#xff0c;大多数可能都有看过阿里的Java后台开发手册&#xff0c;里面有关于Java后台开发规范的一些内容&#xff0c;基本覆盖了一些通用、普适的规…

用IDEA进行Java后台开发(二)

这篇博客接上一篇&#xff0c;用IDEA进行Java后台开发&#xff08;一&#xff09; 上一篇我们已经将工程创建完成了&#xff0c;下面我们将创建一个Servlet&#xff0c;然后本地启动tomcat后调用接口&#xff0c;返回helloWorld&#xff0c;过程如下&#xff1a; 1.在src目录…

Java后台开发入门

转载自: 梦回河口&#xff1a;http://blog.csdn.net/zxc514257857/article/details/71152003 1&#xff0c;什么是Web应用程序 Web应用程序是可以通过Web进行访问的应用程序&#xff0c;最大的好处是访问非常容易。比如淘宝、新浪、网易等门户网站   软件开发领域的三大方向…

Java后台开发:环境搭建

jdkeclipsesvnmavenmysqltomcat7.0sublime安装包和jar插件 配置管理工具-SVN http://download.csdn.net/detail/u013142781/9355417 tomcatPluginV331 http://download.csdn.net/detail/u013142781/9355409 Sublime_Text_2.0.2 http://download.csdn.net/detail/u013142781/…

【Java后台】从零开始的Java后台开发(三)

编写基础的Servlet应用程序 1 Servlet 使用Servlet需要在pom.xml中引入以下依赖&#xff1a; <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --><dependency><groupId>javax.servlet</groupId><artifactId>javax.…