奇小葩讲设备树(3/5)-- Linux设备树详解(三)u-boot设备树的传递
前面两节介绍了设备的基本概念、编译、结构的组成,本章讨论的主要内容为
- dtb如何通过Bootloader引导程序加载到内核
- bootloader如何解析dbt
- bootloader支持哪些dtb的操作
1. 传递dtb给内核
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
对于我们下载dtb的地址0x32000000,这个地址有什么要求呢?是随意选的地址就可以,还是要遵循什么原则呢?
- 不要破坏u-boot本身
- 不要破坏内核本身: 内核本身的空间不能占用, 内核要用到的内存区域也不能占用
对于该问题,我们拿了一块2440的地址空间分配图来说明该问题
对于dtb的存放,只能存放在空闲区,并且不能与其他区有重合的地方。
2. fdt命令查看设备树
- 修改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的支持
现在的uboot已经做得和kernel很像,最主要的一点是,uboot也使用了dtb的方法,将设备树和代码分离开来(当然可以通过宏来控制)。
CONFIG_OF_CONTROL=y
// 用于表示是否使用了dtb的方式CONFIG_OF_SEPARATE=y
// 是否将dtb和uboot分离表一
4. 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,
...
}
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分配新的内存地址空间
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. 参考文档:
奇小葩讲设备树(3/5)-- Linux设备树详解(三)u-boot设备树的传递相关推荐
- 奇小葩讲设备树(4/5)-- Linux设备树详解(四)kernel的解析
uboot将一些参数,设备树文件传给内核,那么内核如何处理这些设备树文件呢?本章就kernel解析设备树的过程和原理,本章的主要内容以Device Tree相关的数据流分析为索引,对ARM linux ...
- 奇小葩讲设备树(1/5)-- Linux设备树详解(一) 基础知识
关于设备树,之前就经过详细的系统培训,但是本着会用就行的原则,对各个知识点都没有进行系统的总结.都是用到哪里学哪里,时间长了,基本也忘记了.所以对于后期知识各个知识点进行总结,本章主要讨论一下内容,能 ...
- 奇小葩讲设备树(5/5)-- Linux设备树详解(五)设备树的使用
对于任何的知识来说,了解了理论的知识,知道了设备树怎么解析用以代替传统的范式之后,我们需要知道怎么使用设备树.对于使用我们分两部分,一部分是它有哪些接口,能做些什么,至于怎么编写dts文件本章不讨论. ...
- 奇小葩讲设备树(2/5)-- Linux设备树详解(二)文件构成
设备树就是描述单板资源以及设备的一种文本文件.至于出现的原因,基本的语法和使用方法,上一章节做了基本的介绍.本篇文章主要是更深层次的探讨设备文件的构成. 1. devie tree的编译 Device ...
- Linux字符设备驱动详解七(“插件“设备树实现RGB灯驱动)
文章目录 系列文章目录 前言 正文 Device Tree Overlays:"插件"设备树 传统设备树 "插件"设备树 使用前提 案例说明 设备树:foo.d ...
- 【Linux驱动开发】设备树详解(二)设备树语法详解
活动地址:CSDN21天学习挑战赛 [Linux驱动开发]设备树详解(一)设备树基础介绍 [Linux驱动开发]设备树详解(二)设备树语法详解 [Linux驱动开发]设备树详解(三)设备树Kern ...
- linux驱动开发篇(三)—— 总线设备驱动模型
linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...
- 树链剖分之重链剖分详解
树链剖分之重链剖分详解 一些概念 算法讲解 应用 求最近公共祖先 对树上的一条链进行修改和查询 相关练习题 一些概念 在学习重链剖分前,首先要明白以下几个概念: 中二重儿子:就是一个节点的儿子中最&q ...
- 目录树 删除 数据结构_数据结构:B树和B+树的插入、删除图文详解
B树 1.1B树的定义 B树也称B-树,它是一颗多路平衡查找树.我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数.当m取2时,就是我们常见的二叉搜索树. ...
最新文章
- 「百度@了所有车企」:Apollo自动驾驶商用上岗,欢迎上车
- 修改otrs notification master邮件通知地址和名称
- SQL中的left outer join,inner join,right outer join用法
- ASP.NET中数据有效性校验的方法
- js调用vlc_如何使用HTML5或JavaScript查看RTSP流,而不使用Real Player插件上的VLC插件等插件?...
- 7-1 公路村村通 (30 分)
- Hadoop系列-HDFS HA高可用集群
- [Swift]LeetCode46. 全排列 | Permutations
- python image.open函数_PIL.Image.open和cv2.imread的比较与相互转换的方法
- cad编辑节点快捷键是什么_CAD所有快捷键
- html用css设置图片大小,css如何设置图片大小?
- 招聘面试的套路与原则
- JDBC部署到Tomcat驱动加载失败
- U盘(移动硬盘)介质写入保护解决办法
- 用python祝节日快乐_节日快乐祝福语有哪些?
- 【C语言程序设计】C语言求最小公倍数(详解版)!
- ROG 570-E 主板问题:主板发出1长2短的滴滴声,黄灯常亮
- Visual C++ 2008入门经典 Ivor Horton(书_在线阅读)
- 西湖大学人工智能与生物医学影像实验室招聘科研助理及博士后
- K8S 之 Pod定义梳理
热门文章
- css 选择href属性值,巧用CSS属性值正则匹配选择器(小技巧)
- mac使用被动ftp模式(pasv)_ftp主动模式和被动模式
- map怎么转化dto_阿里面试题:为什么Map桶中个数超过8才转为红黑树
- 跨域失败 过滤器_跨域问题解决方案
- php数组foreach循环添加键值对_在PHP的foreach循环中插入一个$key作为变量
- java 父类 超类_Java超类-java.lang.object
- c语言文件打开函数,C语言fopen函数中文件打开方式(参数值)
- Failed to load resource: the server responded with a status of 500 (Internal Server Error)
- Python--三元表达式、列表推导式、生成器表达式、递归、匿名函数、内置函数...
- Mac下Nginx、PHP、MySQL 和 PHP-fpm安装配置