前面两节介绍了设备的基本概念、编译、结构的组成,本章讨论的主要内容为

  1. dtb如何通过Bootloader引导程序加载到内核
  2. bootloader如何解析dbt
  3. bootloader支持哪些dtb的操作

1. 传递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三个寄存器的设置

  1. r0一般设置为0;
  2. r1一般设置为machine id (在使用设备树时该参数没有被使用);是让内核知道是哪个CPU,从而调用对应的初始化函数
  3. 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

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

  1. 不要破坏u-boot本身
  2. 不要破坏内核本身: 内核本身的空间不能占用, 内核要用到的内存区域也不能占用

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

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

2. fdt命令查看设备树

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

  1. 修改dts文件,重新编译得到dtb并上传烧写
  2. 使用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的镜像中

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

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

4. uboot中如何获取dtb

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

  1. 获取dtb的地址,并且验证dtb的合法性
  2. 根据你编译的是集成还是分离,如果是集成的话,需要为dtb预留内存空间并进行relocate
  3. 重新获取一次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();
}

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

  1. 对于集成的dtb的u-boot,使用__dtb_dt_begin符号来获取dtb地址,如果是分离式的,通过_end符号来获取dtb地址,同时也支持通过环境参数fdtcontroladdr来配置
  2. 然后通过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;
}

5. 参考文档:

https://blog.csdn.net/kunkliu/article/details/82707282
https://blog.csdn.net/thisway_diy/article/details/84338249

奇小葩讲设备树(3/5)-- Linux设备树详解(三)u-boot设备树的传递相关推荐

  1. 奇小葩讲设备树(4/5)-- Linux设备树详解(四)kernel的解析

    uboot将一些参数,设备树文件传给内核,那么内核如何处理这些设备树文件呢?本章就kernel解析设备树的过程和原理,本章的主要内容以Device Tree相关的数据流分析为索引,对ARM linux ...

  2. 奇小葩讲设备树(1/5)-- Linux设备树详解(一) 基础知识

    关于设备树,之前就经过详细的系统培训,但是本着会用就行的原则,对各个知识点都没有进行系统的总结.都是用到哪里学哪里,时间长了,基本也忘记了.所以对于后期知识各个知识点进行总结,本章主要讨论一下内容,能 ...

  3. 奇小葩讲设备树(5/5)-- Linux设备树详解(五)设备树的使用

    对于任何的知识来说,了解了理论的知识,知道了设备树怎么解析用以代替传统的范式之后,我们需要知道怎么使用设备树.对于使用我们分两部分,一部分是它有哪些接口,能做些什么,至于怎么编写dts文件本章不讨论. ...

  4. 奇小葩讲设备树(2/5)-- Linux设备树详解(二)文件构成

    设备树就是描述单板资源以及设备的一种文本文件.至于出现的原因,基本的语法和使用方法,上一章节做了基本的介绍.本篇文章主要是更深层次的探讨设备文件的构成. 1. devie tree的编译 Device ...

  5. Linux字符设备驱动详解七(“插件“设备树实现RGB灯驱动)

    文章目录 系列文章目录 前言 正文 Device Tree Overlays:"插件"设备树 传统设备树 "插件"设备树 使用前提 案例说明 设备树:foo.d ...

  6. 【Linux驱动开发】设备树详解(二)设备树语法详解

    ​ 活动地址:CSDN21天学习挑战赛 [Linux驱动开发]设备树详解(一)设备树基础介绍 [Linux驱动开发]设备树详解(二)设备树语法详解 [Linux驱动开发]设备树详解(三)设备树Kern ...

  7. linux驱动开发篇(三)—— 总线设备驱动模型

    linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...

  8. 树链剖分之重链剖分详解

    树链剖分之重链剖分详解 一些概念 算法讲解 应用 求最近公共祖先 对树上的一条链进行修改和查询 相关练习题 一些概念 在学习重链剖分前,首先要明白以下几个概念: 中二重儿子:就是一个节点的儿子中最&q ...

  9. 目录树 删除 数据结构_数据结构:B树和B+树的插入、删除图文详解

    B树 1.1B树的定义 B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数.当m取2时,就是我们常见的二叉搜索树. ...

最新文章

  1. 「百度@了所有车企」:Apollo自动驾驶商用上岗,欢迎上车
  2. 修改otrs notification master邮件通知地址和名称
  3. SQL中的left outer join,inner join,right outer join用法
  4. ASP.NET中数据有效性校验的方法
  5. js调用vlc_如何使用HTML5或JavaScript查看RTSP流,而不使用Real Player插件上的VLC插件等插件?...
  6. 7-1 公路村村通 (30 分)
  7. Hadoop系列-HDFS HA高可用集群
  8. [Swift]LeetCode46. 全排列 | Permutations
  9. python image.open函数_PIL.Image.open和cv2.imread的比较与相互转换的方法
  10. cad编辑节点快捷键是什么_CAD所有快捷键
  11. html用css设置图片大小,css如何设置图片大小?
  12. 招聘面试的套路与原则
  13. JDBC部署到Tomcat驱动加载失败
  14. U盘(移动硬盘)介质写入保护解决办法
  15. 用python祝节日快乐_节日快乐祝福语有哪些?
  16. 【C语言程序设计】C语言求最小公倍数(详解版)!
  17. ROG 570-E 主板问题:主板发出1长2短的滴滴声,黄灯常亮
  18. Visual C++ 2008入门经典 Ivor Horton(书_在线阅读)
  19. 西湖大学人工智能与生物医学影像实验室招聘科研助理及博士后
  20. K8S 之 Pod定义梳理

热门文章

  1. css 选择href属性值,巧用CSS属性值正则匹配选择器(小技巧)
  2. mac使用被动ftp模式(pasv)_ftp主动模式和被动模式
  3. map怎么转化dto_阿里面试题:为什么Map桶中个数超过8才转为红黑树
  4. 跨域失败 过滤器_跨域问题解决方案
  5. php数组foreach循环添加键值对_在PHP的foreach循环中插入一个$key作为变量
  6. java 父类 超类_Java超类-java.lang.object
  7. c语言文件打开函数,C语言fopen函数中文件打开方式(参数值)
  8. Failed to load resource: the server responded with a status of 500 (Internal Server Error)
  9. Python--三元表达式、列表推导式、生成器表达式、递归、匿名函数、内置函数...
  10. Mac下Nginx、PHP、MySQL 和 PHP-fpm安装配置