• 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

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

     

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

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

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

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

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

    1. struct fdt_header {
    2. fdt32_t magic; /* magic word FDT_MAGIC */
    3. fdt32_t totalsize; /* total size of DT block */
    4. fdt32_t off_dt_struct; /* offset to structure */
    5. fdt32_t off_dt_strings; /* offset to strings */
    6. fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
    7. fdt32_t version; /* format version */
    8. fdt32_t last_comp_version; /* last compatible version */
    9. /* version 2 fields below */
    10. fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
    11. booting on */
    12. /* version 3 fields below */
    13. fdt32_t size_dt_strings; /* size of the strings block */
    14. /* version 17 fields below */
    15. fdt32_t size_dt_struct; /* size of the structure block */
    16. };
    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的大小

    调用关系如下。

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

     

  • 相关阅读:
    shiro-反序列化漏洞
    【数模/评价模型】层次分析
    软件测试W模型
    2022Java最新学习路线(初学者必看)
    使用git-repo管理多个git仓库
    Linux命令(77)之curl
    Android | 再探 RecyclerView 之名词解析
    【操作系统一】图解TCP/IP模型+实战
    【Day3】最长上升子序列|Python
    安全!稳定!可信!选OceanBase就对了
  • 原文地址:https://blog.csdn.net/qq_33141353/article/details/127563813