4.2uboot对设备树的支持——dtb的修改原理

本节说明在uboot中修改dtb的原理

在uboot中,有一些命令支持对dtb文件进行修改

当我们想要修改dtb文件时,可以直接修改dts文件,然后编译dts文件生成新的dtb文件,再将新的dtb文件载入设备

或者,我们也可以在uboot中使用命令,直接修改dtb文件。修改完成后,再将新的dtb文件保存在板子上,以后启动时就可以使用这个新的dtb文件了。

实际上,在uboot中修改dtb的命令就是fdt。

fdt命令在最新的uboot版本中已经有支持了,但是1.1.6版本还不支持,所以需要修改uboot代码,增加支持fdt指令(从新版本中移植)。

在移植之前,先了解一下在fdt指令修改dtb的原理。

在dtb文件中修改某个属性

以修改dtb文件中的某个属性的原理为例,开始说明fdt指令修改dtb的原理。

先来回顾一下dtb文件中,属性是怎么保存的

首先是表示属性开始的token FDT_PROP(0x00000003);然后是描述该属性信息的extra data(len+nameoff);最后是value,也就是属性值,属性值的长度就是len

struct {
    uint32_t len;        //以字节为单位记录了属性值的长度(长度可能为0,表示一个空值);
    uint32_t nameoff;    //表示属性名在string block中的偏移位置;
};

 

那么,如何修改这个prop的值呢

假设,旧值长度len新值长度new_len,且new_len > len

那么,需要把原来的val所占的空间扩展一下,扩展为new_len;并且,在原来的val后面的数据,都需要往后移动一下,移动的长度为(new_len - len);移动之后,就可以把新的val写入了。

同时,属性值的长度也要更新为new_len

 另外,头部信息也需要同步修改

struct fdt_header {
	fdt32_t magic;			 /* magic word FDT_MAGIC */
	fdt32_t totalsize;		 /* total size of DT block */
	fdt32_t off_dt_struct;		 /* offset to structure */
	fdt32_t off_dt_strings;		 /* offset to strings */
	fdt32_t off_mem_rsvmap;		 /* offset to memory reserve map */
	fdt32_t version;		 /* format version */
	fdt32_t last_comp_version;	 /* last compatible version */

	/* version 2 fields below */
	fdt32_t boot_cpuid_phys;	 /* Which physical CPU id we're
					    booting on */
	/* version 3 fields below */
	fdt32_t size_dt_strings;	 /* size of the strings block */

	/* version 17 fields below */
	fdt32_t size_dt_struct;		 /* size of the structure block */
};
  1. magic:不需要修改;
  2. totalsize:需要修改,总大小增加了(new_len - len)字节;
  3. off_dt_struct:不需要修改;
  4. off_dt_string:需要修改,偏移值增加了(new_len - len)字节;
  5. off_mem_rsvmap:不需要更改;
  6. version:不需要更改;
  7. last_comp_version:不需要更改;
  8. boot_cpuid_phys:不需要更改;
  9. size_dt_strings:不需要更改;
  10. size_dt_struct:需要更改,struct块的size增加了(new_len - len)字节;

也就是说,头部信息中有三项需要同步修改。

 dtb文件的布局:

 总结一下,修改属性的值,老值为len,新值为new_len,且new_len > len,需要以下几步:

  1. 把原属性val所占空间从len字节扩展为new_len字节:
    把老值之后的所有内容向后移动(new_len - len)字节;
  2. 把新值写入val所占的newlen字节空间,更新属性值的长度信息;
  3. 修改dtb头部信息中structure block的长度: size_dt_struct;
  4. 修改dtb头部信息中string block的偏移值: off_dt_strings;
  5. 修改dtb头部信息中的总长度: totalsize;

在dtb文件中增加一个新的属性

了解了dtb文件中属性的修改原理之后,其实其他的操作也是类似的。

比如,在某个节点中增加一个新的属性

  1. 如果在string block中没有这个属性的名字,就在string block尾部添加一个新字符串:
    属性的名字;
    并且修改dtb头部信息中string block的长度:size_dt_strings;
    修改dtb头部信息中的总长度: totalsize;

  2. 找到属性所在节点, 在节点尾部扩展一块空间, 内容及长度为(12+len):
    TAG      // 4字节, 对应0x00000003
    len      // 4字节, 表示属性的val的长度
    nameoff  // 4字节, 表示属性名的offset
    val      // len字节, 用来存放val

  3. 修改dtb头部信息中structure block的长度: size_dt_struct;

  4. 修改dtb头部信息中string block的偏移值: off_dt_strings;

  5. 修改dtb头部信息中的总长度: totalsize;

代码

从uboot官方下载地址(Index of /pub/u-boot/)下载一个支持fdt指令的版本的uboot(u-boot-2018.11-rc2.tar.bz2)。

解压创建工程,在cmd目录下有一个fdt.c文件。

里面有构造一个fdt的指令。

并且还有fdt指令的用法说明。

设置某个节点(path)的属性(prop)等于val为例,看一下代码上是怎么实现的。

fdt指令的入口函数是do_fdt

进入fdt,找到对应的设置属性的值的处理代码。

处理流程如下:

  1. 找到对应节点的偏移值,即找到对应节点(fdt_path_offset);
  2. 找到节点中的对应属性(fdt_getprop);
  3. 若节点中已经存在该属性,将旧的属性值保存下来;
  4. 解析传入的新的属性值,将新的属性值转换成字节流(fdt_parse_prop);
  5. 设置新的属性值(fdt_setprop);

先简单看一下fdt_parse_prop函数,它的作用是将传入的属性值转换成字节流

根据注释信息可以知道,传入的属性值可以是<>指定的十六进制数,也可以是[]指定的字节流,还可以是""指定的字符串

最终它们都要被转换成字节流。

再来看一下fdt_setprop函数,它的作用是设置新的属性值

fdt_setprop_placeholder函数中,调用fdt_resize_property_函数,重新规划dtb的大小

调用关系如下。

fdt_setprop
	fdt_setprop_placeholder       // 为新值在DTB中腾出位置
			 fdt_get_property_w  // 得到老值的长度 oldlen
			 fdt_splice_struct_  // 腾空间
					fdt_splice_  // 使用memmove移动DTB数据, 移动(newlen-oldlen)
					fdt_set_size_dt_struct  // 修改DTB头部, size_dt_struct
					fdt_set_off_dt_strings  // 修改DTB头部, off_dt_strings
					
	memcpy(prop_data, val, len);  // 在DTB中存入新值

 

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值