设备树叠加层

news2024/9/25 9:35:05

设备树覆盖

设备树 (DT)是描述不可发现硬件的命名节点和属性的数据结构。内核(例如 Android 中使用的 Linux 内核)使用 DT 来支持 Android 设备使用的各种硬件配置。硬件供应商提供他们自己的设备树源 (DTS)文件,这些文件使用设备树编译器编译成设备树 Blob (DTB)文件。引导加载程序随后使用这些文件。 DTB 文件包含一个二进制格式的扁平化设备树

设备树覆盖 (DTO)使中央设备树 blob (DTB) 能够覆盖在设备树上。使用 DTO 的引导加载程序可以维护片上系统 (SoC) DT 并动态覆盖特定于设备的 DT,向树中添加节点并更改现有树中的属性。

DTBO用于覆盖的设备树 Blob

Android 9 版本中的更新

在 Android 9 中,引导加载程序在将统一设备树 blob 传递给内核之前不得修改设备树覆盖中定义的属性。

加载设备树

在引导加载程序中加载设备树涉及构建、分区和运行。

图 1.在引导加载程序中加载设备树的典型实现。

  1. 要创建并刷新设备树 blob:

    1a.使用设备树编译器 ( dtc >) 将设备树源代码 ( .dts ) 编译成设备树 blob ( .dtb )。设备树 blob 被格式化为扁平设备树。 1b.将.dtb文件闪存到引导加载程序运行时可访问的位置(详见下文)。

  2. 要分区,请确定闪存中引导加载程序运行时可访问且受信任的位置以放置.dtb 。示例位置:

    引导分区

    图 2.通过附加到image.gz并作为“ kernel ”传递给mkbootimg ,将.dtb放入启动分区。

    唯一分区

    图 3..dtb放在一个唯一的分区中(例如dtb分区)。

  3. 要加载设备树 blob 并启动内核:

    3a. .dtb从存储加载到内存中。 3b.使用加载的 DT 的内存地址启动内核。

实现 DTO

实现 DTO 包括分割设备树、构建、分区和运行。在实现可以正常工作之后,您还必须保持两个 DT 之间的兼容性,并确定用于确保每个 DT 分区安全性的策略。

分割 DT

首先将设备树分割成两 (2) 部分:

  • 主 DT。由 SoC 供应商提供的仅限 SoC 访问的部分和默认配置。
  • 叠加 DT。由原始设计制造商 (ODM)/原始设备制造商 (OEM) 提供的设备专用配置。

分割设备树之后,您必须确保主 DT 和叠加 DT 之间的兼容性,以便通过合并主 DT 和叠加 DT 为设备生成完整的 DT。有关 DTO 格式和规则的详细信息,请参阅 DTO 语法。如需详细了解多个设备树,请参阅多个 DT。

构建主 DT 和叠加 DT

如需构建主 DT,请执行以下操作:

  1. 将主 DT .dts 编译为 .dtb 文件。
  2. 将 .dtb 文件刷写到引导加载程序在运行时可访问的分区(详见下文)。

如需构建叠加 DT,请执行以下操作:

  1. 将叠加 DT .dts 编译为 .dtbo 文件。虽然此文件格式与格式设为扁平化设备树的 .dtb 文件相同,但是用不同的文件扩展名可以将其与主 DT 区分开来。
  2. 将 .dtbo 文件刷写到引导加载程序在运行时可访问的分区(详见下文)。

如需详细了解如何使用 DTC 进行编译并在主机上验证 DTO 结果,请参阅编译和验证。

对 DT 进行分区

在闪存中确定引导加载程序在运行时可访问和可信的位置信息以放入 .dtb 和 .dtbo

主 DT 的示例位置:

  • 启动分区的一部分,已附加到内核 (image.gz)。
  • 单独的 DT blob (.dtb),位于专用分区 (dtb)。

叠加 DT 的示例位置:

专属分区

图 1. 将 .dtbo 放在专属的分区(例如 dtbo 分区)中。

ODM 分区

图 2. 将 .dtbo 放入 odm 分区中(仅在您的引导加载程序能够从 odm 分区的文件系统中加载数据时才这样做)。

注意:叠加 DT 分区的大小取决于设备和主 DT blob 上所需的更改量。通常,8 MB 已足够当前使用并已为未来扩展留出了空间(如果需要的话)。

对于支持无缝 (A/B) 更新的设备,请用 A/B 来标识主 DT 和叠加 DT 分区:

示例 1

图 3. DTBO 分区 A/B,示例 1。

示例 2

图 4. DTBO 分区 A/B,示例 2。

在引导加载程序中运行

如需运行,请执行以下操作:

图 5. 引导加载程序中设备树叠加层的典型运行时实现。

  1. 将 .dtb 从存储空间加载到内存中。
  2. 将 .dtbo 从存储空间加载到内存中。
  3. 用 .dtb 叠加 .dtbo 以形成合并的 DT。
  4. 启动内核(已给定合并 DT 的内存地址)。

保持兼容性

主 DTB(来自 SoC 供应商)会被视为 DTBO 的 API 表面。将设备树分离为 SoC 通用部件和设备专用部件后,您必须确保这两个部件以后相互兼容,包括:

  • 主 DT 中的 DT 定义(例如,节点、属性、标签)。主 DT 中的任何定义更改都可能会触发叠加 DT 中的更改。例如,如需更正主 DT 中的某个节点名称,请定义映射到原始节点名称的“别名”标签(以免更改叠加 DT)。
  • 叠加 DT 的存储位置(例如,分区名称、存储格式)。

确保安全

引导加载程序必须确保 DTB/DTBO 安全无虞、未被修改且未被损坏。您可以使用任何解决方案来保护 DTB/DTBO,例如,VBoot 1.0 中的启动映像签名或 AVB 哈希页脚 (VBoot 2.0)。

  • 如果 DTB/DTBO 位于专属的分区中,您可以将该分区添加到 AVB 的信任链。信任链从硬件保护的信任根开始,并进入引导加载程序,从而验证 DTB/DTBO 分区的完整性和真实性。
  • 如果 DTB/DTBO 位于现有分区(如 odm 分区)中,该分区应位于 AVB 的信任链中。(DTBO 分区可以与 odm 分区共享一个公钥)。

DTO 语法

 

设备树源 (DTS) 格式是设备树的文本表示形式。设备树编译器 (DTC) 可将这种格式处理为二进制设备树,这是 Linux 内核要求的形式。

使用引用

DTC(设备树编译器 + 叠加补丁程序)项目在 dtc-format.txt 和 manual.txt 中说明了 DTS 格式。在 dt-object-internal.txt 中说明了 DTO 格式和规则。这些文档说明了如何使用叠加 DT 中的节点 fragment@x 和语法 __overlay__ 更新主 DT。例如:

/ {
  fragment@0 {
    target = <&some_node>;
      __overlay__ {
        some_prop = "okay";
        ...
      };
  };
};

不过,Google 强烈建议您不要使用 fragment@x 和语法 __overlay__,而应使用引用语法。例如:

&some_node {
  some_prop = "okay";
  ...
};

dtc 会将引用语法编译成与上述使用语法 __overlay__ 所生成的对象相同的对象。此语法不强制您对片段进行编号,让您能够轻松地读取和写入叠加 DTS。如果您的 dtc 不支持此语法糖,请使用 AOSP 中的 dtc。

使用标签

为了允许对编译时不存在的节点进行未定义的引用,叠加 DT .dts 文件的头文件中必须带有 /plugin/ 标签。例如:

/dts-v1/;
/plugin/;

在这里,您可以使用引用定位要叠加的节点,该引用是以“和”符号 (&) 作为前缀的绝对节点路径。例如,对于主 DT 中的 node@0

在主 DT 中定义标签......然后使用标签。

[my_main_dt.dts]

/dts-v1/;

/ {
  my_node: node@0 {
    status = "disabled";

    my_child: child@0 {
      value = <0xffffffff>;
    };
  };
};

[my_overlay_dt.dts]

/dts-v1/;
/plugin/;

&my_node {
  status = "okay";
};

&my_child {
  value = <0x1>;
};

叠加

如果引用目标属性存在于主 DT 中,则在 DTO 之后被叠加;否则,系统会对其进行附加。例如:

main.dtsoverlay.dts合并结果

[my_main_dt.dts]

/dts-v1/;

/ {
  compatible = "corp,foo";

  my_node: node@0 {
    status = "disabled";
  };
};

[my_overlay_dt.dts]

/dts-v1/;
/plugin/;

&my_node {
  status = "okay";
};

/dts-v1/;

/ {
  compatible = "corp,foo";

  ...

  node@0 {
    linux,phandle = <0x1>;
    phandle = <0x1>;
    status = "okay";
  };
};

附加

如果引用目标属性不存在于主 DT 中,则在 DTO 之后被附加。例如:

main.dtsoverlay.dts合并结果

[my_main_dt.dts]

/dts-v1/;

/ {
  compatible = "corp,foo";

  my_node: node@0 {
    status = "okay";
  };
};

[my_overlay_dt.dts]

/dts-v1/;
/plugin/;

&my_node {
  new_prop = "bar";
};

/dts-v1/;

/ {
  compatible = "corp,foo";

  ...

  node@0 {
    linux,phandle = <0x1>;
    phandle = <0x1>;
    status = "okay";
    new_prop = "bar";
  };
};

子节点

子节点语法示例:

main.dtsoverlay.dts合并结果

[my_main_dt.dts]

/dts-v1/;

/ {
  compatible = "corp,foo";

  my_nodes: nodes {
    compatible = "corp,bar";

    node@0 {
      status = "disabled";
    };
  };
};

[my_overlay_dt.dts]

/dts-v1/;
/plugin/;

&my_nodes {
  new_prop1 = "abc";

  node@0 {
    status = "okay";
    new_prop2 = "xyz";
  };
};

/dts-v1/;

/ {
  compatible = "corp,foo";

  ...

  nodes {
    linux,phandle = <0x1>;
    phandle = <0x1>;
    compatible = "corp,bar";
    new_prop1 = "abc";

    node@0 {
      linux,phandle = <0x2>;
      phandle = <0x2>;
      status = "okay";
      new_prop2 = "xyz";
    };
  };
};

编译和验证

您可以使用设备树编译器 (DTC) 编译设备树源文件。不过,在将叠加 DT 应用到目标主 DT 之前,您还应该通过模拟 DTO 的行为来验证结果。

通过 DTC 进行编译

使用 dtc 编译 .dts 时,您必须添加选项 -@ 以在生成的 .dtbo 中添加 __symbols__ 节点。__symbols__ 节点包含所有带标签节点的列表,DTO 库可使用这个列表作为参考。

构建主 DT .dts 的示例命令:

<span style="color:var(--devsite-code-color)">dtc -@ -O dtb -o my_main_dt.dtb my_main_dt.dts
</span>

构建叠加 DT .dts 的示例命令:

<span style="color:var(--devsite-code-color)">dtc -@ -O dtb -o my_overlay_dt.dtbo my_overlay_dt.dts
</span>

注意:如果遇到 DTC 构建错误 invalid option --'@',则表示您可能需要更新 DTC 版本。在 AOSP 上游,官方 DTC 对 DTO 的支持从版本 1.4.4 开始,而且大部分补丁程序在 2016 年 12 月后就完成了合并。为了支持 DTO,建议您使用 AOSP 中的 external/dtc,它已与最新的 DTC 同步(已视需要合并 DTO 补丁程序)。

在主机上验证 DTO 结果

验证流程可以帮助您确认将叠加 DT 放在主 DT 上时可能出现的错误。更新目标之前,您可以在 .dts 中使用 /include/ 来模拟 DTO 行为,通过这种方式在主机上验证叠加 DT 的结果。

注意/include/ 不支持在叠加 DT 源中使用 __overlay__

图 1. 使用语法 /include/ 模拟主机上的 DTO

  1. 创建叠加层 .dts 的副本。在副本中,移除第一行头文件。示例:
    /dts-v1/;
    /plugin/;
    
    将文件另存为 my_overlay_dt_wo_header.dts(或您想使用的任何文件名)。
  2. 创建主 .dts 的副本。在副本中的最后一行后,为您在第 1 步中创建的文件附加 include 语法。例如:
    /include/ "my_overlay_dt_wo_header.dts"
    
    将文件另存为 my_main_dt_with_include.dts(或您希望的任何文件名)。
  3. 使用 dtc 编译 my_main_dt_with_include.dts 以获得合并的 DT,这应该与使用 DTO 进行编译所得到的结果相同。例如:
    <span style="color:var(--devsite-code-color)">dtc -@ -O dtb -o my_merged_dt.dtb my_main_dt_with_include.dts
    </span>
  4. 使用 dtc 转储 my_merged_dt.dto
    <span style="color:var(--devsite-code-color)">dtc -O dts -o my_merged_dt.dts my_merged_dt.dtb
    </span>

在 Android 9 中验证 DTO

Android 9 需要具有设备树 Blob 叠加层 (DTBO) 分区。要在 SoC DT 中添加节点或更改属性,引导加载程序必须在 SoC DT 之上动态叠加设备专用的 DT。

指示已应用的叠加层

为了保证供应商测试套件 (VTS) 能够评估叠加应用的正确性,供应商必须添加新的内核命令行参数 androidboot.dtbo_idx 来指示从 DTBO 分区中选择的叠加层。在内核版本为 5.10 或更高版本的 Android 12 环境中,此参数会通过 bootconfig 传递。例如,参数 androidboot.dtbo_idx=x,y,z 将 xy 和 z 报告为 DTBO 分区中已由引导加载程序按相同顺序应用于基础设备树 (DT) 的设备树叠加层 (DTO) 的索引,这些索引以零为起点。

叠加层可以应用于主设备树中的节点,也可以添加新节点,但不能引用之前叠加层中添加的节点。这种限制是必要的,因为叠加应用不会将叠加层符号表与主 DT 符号表合并(不合并的做法既可避免符号名称出现冲突,也可避免叠加层之间的依赖关系复杂化)。

示例:无效叠加层

在此示例中,overlay_2.dts 引用了由 overlay_1.dts 添加的节点 e。在将 overlay_1 应用于主 DT 之后,如果尝试将 overlay_2 应用于生成的 DT,叠加应用将会运行失败,并显示基础 DT 的符号表中不存在符号 e 的错误。

main.dtsoverlay_1.dtsoverlay_2.dts
[main.dts]

/dts-v1/;

/ {
  a: a {};
  b: b {};
  c: c {};
};
[overlay_1.dts]

/dts-v1/;
/plugin/;

&b { ref1 =  <&a>;
    e: e {
        prop = <0x0a>;
        phandle = <0x04>;
    };
};
[overlay_2.dts]

/dts-v1/;
/plugin/;

/* invalid! */
&e {
    prop = <0x0b>;
};

示例:有效叠加层

在此示例中,overlay_2.dts 仅引用了主 DTS 中的节点 b。将 overlay_1 和 overlay_2 依次应用于基础 DT 之后,节点 e 中属性 prop 的值(由 overlay_1.dts 设置)将被 overlay_2.dts 设置的值覆盖。

main.dtsoverlay_1.dtsoverlay_2.dts
[final.dts]

/dts-v1/;

/ {
  a: a {};
  b: b {};
  c: c {};
};
[overlay_1.dts]

/dts-v1/;
/plugin/;

&b { ref1 =  <&a>;
     e {
          prop = <0x0c>;
      };
};
[overlay_2.dts]

/dts-v1/;
/plugin/;

/* valid */
&b { ref1 =  <&c>;
     e {
          prop = <0x0d>;
      };
};

实现 DTBO 分区

要实现所需的 DTBO 分区,请确保引导加载程序可以执行以下操作:

  1. 识别它正在哪个开发板上运行,并选择要应用的相应叠加层。
  2. 将 androidboot.dtbo_idx 参数附加到内核命令行。
    • 该参数必须指示 DTBO 分区映像中按相同顺序应用于基础 DT 的 DTO 的索引,这些索引以零为起点。
    • 这些索引必须引用叠加层在 DTBO 分区中的位置。

要详细了解 DTBO 分区结构,请访问 source.android.com 上的设备树叠加层。

验证 DTBO 分区

您可以使用 VTS 验证以下内容:

  • 内核命令行参数 androidboot.dtbo_idx 是否存在(方法:检查 Init 是否已自动设置相应的 ro.boot.dtbo_idx 系统属性)。
  • ro.boot.dtbo_idx 系统属性的有效性(方法:检查该属性是否至少指定了一个有效的 DTBO 映像索引)。
  • DTBO 分区的有效性(还应验证 DTBO 分区中应用于基础 DT 的叠加层的有效性)。
  • 生成的 DT 中的其他节点或属性更改是否已呈现给 Linux 内核。

例如,在以下叠加层和最终 DT 中,将 androidboot.dtbo_idx=5,3 添加到内核命令行可通过验证,而将 androidboot.dtbo_idx=3,5 添加到内核命令行不能通过验证。

索引 3 处的叠加层 DT索引 5 处的叠加层 DT
[overlay_1.dts]

/dts-v1/;
/plugin/;

&c { prop = <0xfe>; };
[overlay_2.dts]

/dts-v1/;
/plugin/;

&c { prop = <0xff>; };
最终 DT
/dts-v1/;
/ {

	a {
		phandle = <0x1>;
	};

	b {
		phandle = <0x2>;
	};

	c {
		phandle = <0x3>;
		prop = <0xfe>;
	};

	__symbols__ {
		a = "/a";
		b = "/b";
		c = "/c";
	};
};

  

 

 使用多个 DT

很多 SoC 供应商和 ODM 都支持在一台设备上使用多个 DT,从而使一个映像能够为多个 SKU/配置提供支持。在这种情况下,引导加载程序会在运行时识别硬件,并加载相应的 DT:

图 1. 引导加载程序中的多个设备树叠加层。

注意:使用多个 DT 不是强制性要求。

设置

如需向 DTO 模型添加对多个 DT 的支持,请设置一个主 DT 列表和另一个叠加 DT 列表。

图 2. 多个 DT 的运行时 DTO 实现。

引导加载程序应该能够:

  • 读取 SoC ID 并选择相应的主设备树,并
  • 读取板 ID 并选择相应的叠加设备树。

只能选择一个主 DT 供在运行时使用。可选择多个叠加 DT,但它们必须与选定的主 DT 兼容。使用多个叠加层有助于避免 DTBO 分区内的每块板上都存储一个叠加层,并能让引导加载程序根据板 ID 或通过探测外设来确定所需叠加层的子集。例如,板 A 可能需要通过叠加层 1、3 和 5 添加的设备,而板 B 可能需要通过叠加层 1、4 和 5 添加的设备。

分区

要进行分区,请在闪存中确定引导加载程序在运行时可访问和可信的位置,以存储 DTB 和 DTBO(引导加载程序必须能够在匹配的进程中找到这些文件)。请记住,DTB 和 DTBO 不能存在于同一个分区中。如果您的 DTB/DTBO 位于 dtb/dtbo 分区中,请使用 DTB/DTBO 分区格式中详细列出的表结构和头文件格式。

在引导加载程序中运行

要运行,请执行以下操作:

  1. 标识 SoC 并将相应的 .dtb 从存储空间加载到内存中。
  2. 标识板并将相应的 .dtbo 从存储空间加载到内存中。
  3. 用 .dtbo 叠加 .dtb 形成合并的 DT。
  4. 启动内核(已给定合并 DT 的内存地址)。

DTB/DTBO 分区

如果您的 DTB/DTBO 位于专属的分区(例如 dtb 和 dtbo 分区)中,请使用以下表格结构和头文件格式:

图 1. dtb/dtbo 分区布局示例(如需了解 AVB 签名相关信息,请参阅安全性)。

数据结构

dt_table_header 适用于 dtb/dtbo 分区;您不能在 image.gz 末尾处附加此格式。如果您有一个 DTB/DTBO,则仍必须使用此格式(并且,dt_table_header 中的 dt_entry_count 为 1)。

#define DT_TABLE_MAGIC 0xd7b7ab1e

struct dt_table_header {
  uint32_t magic;             // DT_TABLE_MAGIC
  uint32_t total_size;        // includes dt_table_header + all dt_table_entry
                              // and all dtb/dtbo
  uint32_t header_size;       // sizeof(dt_table_header)

  uint32_t dt_entry_size;     // sizeof(dt_table_entry)
  uint32_t dt_entry_count;    // number of dt_table_entry
  uint32_t dt_entries_offset; // offset to the first dt_table_entry
                              // from head of dt_table_header

  uint32_t page_size;         // flash page size we assume
  uint32_t version;       // DTBO image version, the current version is 0.
                          // The version will be incremented when the
                          // dt_table_header struct is updated.
};

struct dt_table_entry {
  uint32_t dt_size;
  uint32_t dt_offset;         // offset from head of dt_table_header

  uint32_t id;                // optional, must be zero if unused
  uint32_t rev;               // optional, must be zero if unused
  uint32_t custom[4];         // optional, must be zero if unused
};

如需读取所有 dt_table_entry,请使用 dt_entry_sizedt_entry_count 和 dt_entries_offset。例如:

my_read(entries_buf,
        header_addr + header->dt_entries_offset,
        header->dt_entry_size * header->dt_entry_count);

dt_table_entry 中的 idrevcustom 是设备树的可选硬件标识,引导加载程序可以使用这些标识有效地识别要加载的 DTB/DTBO。如果引导加载程序需要获取更多信息,请将其放在 DTB/DTBO 中,引导加载程序可在这里解析 DTB/DTBO,从而读取这些信息(参见下面的示例代码)。

示例代码

以下示例代码可检查引导加载程序中的硬件标识。

  • check_dtbo() 函数用于检查硬件标识。 首先它会检查结构 dt_table_entry 中的数据(idrev 等)。如果这些数据未能提供充足的信息,它会将 dtb 数据加载到内存中,并检查 dtb 中的值。
  • my_hw_information 和 soc_id 属性的值会在根节点中进行解析(请参见 my_dtbo_1.dts 中的示例)。

    [my_dtbo_1.dts]
    /dts-v1/;
    /plugin/;
    
    / {
      /* As DTS design, these properties only for loader, won't overlay */
      compatible = "board_manufacturer,board_model";
    
      /* These properties are examples */
      board_id = <0x00010000>;
      board_rev = <0x00010001>;
      another_hw_information = "some_data";
      soc_id = <0x68000000>;
      ...
    };
    
    &device@0 {
      value = <0x1>;
      status = "okay";
    };
    
    [my_bootloader.c]
    int check_dtbo(const dt_table_entry *entry, uint32_t header_addr) {
      ...
      if (entry->id != ... || entry->rev != ...) {
        ...
      }
      ...
      void * fdt_buf = my_load_dtb(header_addr + entry->dt_offset, entry->dt_size);
      int root_node_off = fdt_path_offset(fdt_buf, "/");
      ...
      const char *my_hw_information =
        (const char *)fdt_getprop(fdt_buf, root_node_off, "my_hw_information", NULL);
      if (my_hw_information != NULL && strcmp(my_hw_information, ...) != 0) {
        ...
      }
      const fdt32_t *soc_id = fdt_getprop(fdt_buf, root_node_off, "soc_id", NULL);
      if (soc_id != NULL && *soc_id != ...) {
        ...
      }
      ...
    }
    

mkdtimg

mkdtimg 是用于创建 dtb/dtbo 映像的工具(源代码 位于 AOSP 中的 system/libufdt 下)。mkdtimg 支持多个命令,包括 createcfg_create 和 dump

create

使用 create 命令创建 dtb/dtbo 映像:

mkdtimg create <image_filename> (<global-option>...) \
    <ftb1_filename> (<entry1_option>...) \
    <ftb2_filename> (<entry2_option>...) \
    ...

ftbX_filename 会在映像中生成一个 dt_table_entryentryX_option 是分配给 dt_table_entry 的值。这些值可以是以下任一值:

--id=<number|path>
--rev=<number|path>
--custom0=<number|path>
--custom1=<number|path>
--custom2=<number|path>
--custom3=<number|path>

数字值可以是 32 位数字(如 68000)或十六进制数字(如 0x6800)。或者,您也可以使用以下格式指定路径:

<full_node_path>:<property_name>

例如,/board/:idmkdtimg 从 DTB/DTBO 文件中的路径读取值,并将值(32 位)分配给 dt_table_entry 中的相对属性。或者,您也可以将 global_option 作为所有条目的默认选项。dt_table_header 中 page_size 的默认值为 2048;可使用 global_option --page_size=<number> 分配其他值。

例如:

[board1.dts]
/dts-v1/;
/plugin/;

/ {
  compatible = "board_manufacturer,board_model";
  board_id = <0x00010000>;
  board_rev = <0x00010001>;
  another_hw_information = "some_data";
  ...
};

&device@0 {
  value = <0x1>;
  status = "okay";
};

mkdtimg create dtbo.img --id=/:board_id --custom0=0xabc \
  board1.dtbo \
  board2.dtbo --id=0x6800 \
  board3.dtbo --id=0x6801 --custom0=0x123
  • 第一个 dt_table_entry (board1.dtboid 为 0x00010000custom[0] 为 0x00000abc
  • 第二个 id 为 0x00006800custom[0] 为 0x00000abc
  • 第三个 id 为 0x00006801custom[0] 为 0x00000123
  • 所有其他项均使用默认值 (0)。

cfg_create

cfg_create 命令使用以下格式的配置文件创建映像:

# global options
  <global_option>
  ...
# entries
<ftb1_filename>     # comment
  <entry1_option>   # comment
  ...
<ftb2_filename>
  <entry2_option>
  ...
...

选项 global_option 和 entryX_option 必须以一个或多个空格字符开头(这些选项与 create 选项相同,不带 -- 前缀)。空行或者以 # 开头的行将被忽略。

例如:

[dtboimg.cfg]
# global options
  id=/:board_id
  rev=/:board_rev
  custom0=0xabc

board1.dtbo

board2.dtbo
  id=0x6800       # override the value of id in global options

board2.dtbo
  id=0x6801       # override the value of id in global options
  custom0=0x123   # override the value of custom0 in global options

mkdtimg cfg_create dtbo.img dtboimg.cfg

mkdtimg 不会处理 .dtb/.dtbo 文件的对齐方式,而是将它们附加到映像上。使用 dtc 将 .dts 编译为 .dtb/.dtbo 时,必须添加选项 -a。例如,添加选项 -a 4 会增加内边距,使得 .dtb/.dtbo 的大小调整为 4 个字节。

多个 DT 表格条目可以共享一个 .dtb/.dtbo。如果您为不同的条目使用同一个文件名,则系统只会在具有相同 dt_offset 和 dt_size 的映像中存储一份内容。使用具有相同 DT 的不同硬件时,这种方式非常有用。

dump

对于 dtb/dtbo 映像,请使用 dump 命令打印映像中的信息。例如:

mkdtimg dump dtbo.img
dt_table_header:
               magic = d7b7ab1e
          total_size = 1300
         header_size = 32
       dt_entry_size = 32
      dt_entry_count = 3
   dt_entries_offset = 32
           page_size = 2048
             version = 0
dt_table_entry[0]:
             dt_size = 380
           dt_offset = 128
                  id = 00010000
                 rev = 00010001
           custom[0] = 00000abc
           custom[1] = 00000000
           custom[2] = 00000000
           custom[3] = 00000000
           (FDT)size = 380
     (FDT)compatible = board_manufacturer,board_model
...

优化 DTO

本页介绍了可以对 DTO 实现进行的优化,描述了针对叠加根节点的限制,并详细介绍了如何在 DTBO 映像中配置经过压缩的叠加层。此外还提供了示例实现说明和代码。

内核命令行

设备树中的原始内核命令行位于 chosen/bootargs 节点中。引导加载程序必须将此位置与内核命令行的其他源位置串联起来:

/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};

DTO 无法串联主 DT 和叠加 DT 的值,因此您必须将主 DT 的内核命令行置入 chosen/bootargs 中,并将叠加 DT 的内核命令行置入 chosen/bootargs_ext 中。然后,引导加载程序才能串联这些位置,并将结果传递到内核。

main.dtsoverlay.dts

/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};

/dts-v1/;
/plugin/;

&chosen {
  bootargs_ext = "...";
};

libufdt

虽然最新的 libfdt 支持 DTO,但建议使用 libufdt 来实现 DTO(AOSP 源代码位于 platform/system/libufdt 下)。libufdt 可通过扁平化设备树 (FDT) 构建真实的树结构(非扁平化设备树,简称“ufdt”),从而改善合并两个 .dtb 文件的效果(从 O(N2) 到 O(N),其中 N 是树中的节点数)。

性能测试

在 Google 的内部测试中,分别在 2,405 个 .dtb 和 283 个 .dtbo DT 节点上使用 libufdt,在编译后分别会生成 70,618 字节和 8,566 字节的文件。从 FreeBSD 移植的 DTO 实现的运行时为 124 毫秒,相比之下 libufdt DTO 的运行时为 10 毫秒,差异非常明显。

在 Pixel 设备的性能测试中,我们比较了 libufdt 和 libfdt。基本节点数量带来的影响相似,但包含以下差异:

  • 500 次叠加(附加或覆盖)操作具有 6-8 倍的时间差异
  • 1,000 次叠加(附加或覆盖)操作具有 8-10 倍的时间差异

附加计数设置为 X 的示例:

图 1. 附加计数为 X

覆盖计数设置为 X 的示例:

图 2. 覆盖计数为 X

libufdt 是使用一些 libfdt API 和数据结构开发的。使用 libufdt 时,必须包含并关联 libfdt(然而,您可以在代码中使用 libfdt API 来操作 DTB 或 DTBO)。

libufdt DTO API

libufdt 中适用于 DTO 的主要 API 如下:

struct fdt_header *ufdt_apply_overlay(
        struct fdt_header *main_fdt_header,
        size_t main_fdt_size,
        void *overlay_fdt,
        size_t overlay_size);

参数 main_fdt_header 是主 DT,overlay_fdt 是包含 .dtbo 文件内容的缓冲区。返回值是一个包含合并 DT 的新缓冲区(如果出现错误,系统会返回 null)。合并 DT 采用 FDT 格式,您可以在启动内核时将其传递到内核。

返回值的新缓冲区由 dto_malloc() 创建,该缓冲区应在将 libufdt 移植到引导加载程序时实现。有关参考实现,请参阅 sysdeps/libufdt_sysdeps_*.c

根节点限制

您无法将新节点或属性叠加到主 DT 的根节点中,因为叠加操作依赖于标签。由于主 DT 必须定义一个标签,而叠加 DT 则会分配要叠加标签的节点,因此,您无法为根节点提供标签(因而无法叠加根节点)。

SoC 供应商必须定义主 DT 的叠加能力;ODM/OEM 只能使用 SoC 供应商定义的标签附加或覆盖节点。如需解决这个问题,您可以在基础 DT 中的根节点下定义一个 odm 节点,使叠加 DT 中的所有 ODM 节点都能够添加新节点。或者,您也可以将基础 DT 中的所有 SoC 相关节点放在根节点下的 soc 节点中,如下所述:

main.dtsoverlay.dts

/dts-v1/;

/ {
    compatible = "corp,bar";
    ...

    chosen: chosen {
        bootargs = "...";
    };

    /* nodes for all soc nodes */
    soc {
        ...
        soc_device@0: soc_device@0 {
            compatible = "corp,bar";
            ...
        };
        ...
    };

    odm: odm {
        /* reserved for overlay by odm */
    };
};

/dts-v1/;
/plugin/;

/ {
};

&chosen {
    bootargs_ex = "...";
};

&odm {
    odm_device@0 {
        ...
    };
    ...
};

使用经过压缩的叠加层

Android 9 增加了以下支持:在使用第 1 版设备树表格头文件时,在 DTBO 映像中使用经过压缩的叠加层。 使用 DTBO 头文件 v1 时,dt_table_entry 中标记字段的四个最低有效位会指明 DT 条目的压缩格式。

struct dt_table_entry_v1 {
  uint32_t dt_size;
  uint32_t dt_offset;  /* offset from head of dt_table_header */
  uint32_t id;         /* optional, must be zero if unused */
  uint32_t rev;        /* optional, must be zero if unused */
  uint32_t flags;      /* For version 1 of dt_table_header, the 4 least significant bits
                        of 'flags' will be used to indicate the compression
                        format of the DT entry as per the enum 'dt_compression_info' */
  uint32_t custom[3];  /* optional, must be zero if unused */
};

目前,系统支持 zlib 和 gzip 压缩。

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 增加了以下支持:在 VtsFirmwareDtboVerification 测试中测试经过压缩的叠加层,以帮助您验证叠加应用的正确性。

DTO 实现示例

以下说明介绍了使用 libufdt 实现 DTO 的示例过程(示例代码如下)。

示例 DTO 说明

  1. 提供库。如需使用 libufdt,请提供 libfdt 以用于数据结构和 API:

    #include <libfdt.h>
    #include <ufdt_overlay.h>
    
  2. 加载主 DT 和叠加 DT。将 .dtb 和 .dtbo 从存储空间加载到内存中(确切的步骤取决于您的设计)。此时,您应该设置 .dtb/.dtbo 的缓冲区和大小:

    main_size = my_load_main_dtb(main_buf, main_buf_size)
    

    overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
    
  3. 叠加 DT:
    1. 使用 ufdt_install_blob() 获取主 DT 的 FDT 头文件:

      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
      
    2. 对 DTO 调用 ufdt_apply_overlay() 以获取采用 FDT 格式的合并 DT:

      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
      
    3. 使用 merged_fdt 获取 dtc_totalsize() 的大小:

      merged_fdt_size = dtc_totalsize(merged_fdt);
      
    4. 传递合并的 DT 以启动内核:

      my_kernel_entry(0, machine_type, merged_fdt);
      

示例 DTO 代码

#include <libfdt.h>
#include <ufdt_overlay.h>

…

{
  struct fdt_header *main_fdt_header;
  struct fdt_header *merged_fdt;

  /* load main dtb into memory and get the size */
  main_size = my_load_main_dtb(main_buf, main_buf_size);

  /* load overlay dtb into memory and get the size */
  overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);

  /* overlay */
  main_fdt_header = ufdt_install_blob(main_buf, main_size);
  main_fdt_size = main_size;
  merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                  overlay_buf, overlay_size);
  merged_fdt_size = dtc_totalsize(merged_fdt);

  /* pass to kernel */
  my_kernel_entry(0, machine_type, merged_fdt);
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1019331.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UINT64整型数据在格式化时使用了不匹配的格式化符%d导致其他参数无法打印的问题排查

目录 1、问题描述 2、格式化函数内部解析待格式化参数的完整机制说明 2.1、传递给被调用函数的参数是通过栈传递的 2.2、格式化函数是如何从栈上找到待格式化的参数值&#xff0c;并完成格式化的&#xff1f; 2.3、字符串格式化符%s对应的异常问题场景说明 2.4、为了方便…

node 之 express 框架(初级)

一、express 热更新 1、安装扩展 npm install node-dev -D2、在根目录下的 package.json 文件中进行配置 3、之后的启动执行下面的命令即可 npm run dev二、mvc中的 模板引擎 1、ejs模板引擎的安装 npm install ejs -s2、在根目录下的app.js文件中配置 app.set(view engin…

我学编程全靠B站了,真香(第一期)

你好&#xff0c;我是Martin。 我是就读于B站大学2020届的Martin同学&#xff0c;反正我学习计算机真的是全靠 B 站了。 我是个刷视频狂魔&#xff0c;B站收藏夹里也收藏了很多编程类视频&#xff0c; 比如C/C、Go语言、操作系统、数据结构和算法、计算机网络、数据库、Pyth…

深入了解Python运算符和表达式:从基础到高级

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 Python运算符和表达式是…

JavaScript 学习笔记(基础)

其是一门跨平台、面向对象的脚本语言&#xff08;直译型语言&#xff09;&#xff0c;用来控制网页行为&#xff0c;能使网页产生交互效果&#xff01;下面以 JS 代称 JavaScript 引入HTML结构文件有两类方式&#xff1a; 内部脚本 行联式嵌入式外部脚本* 基本语法&#xff1…

npm发布vue3自定义组件库--方法二

npm发布vue3自定义组件库 创建项目 vue create test-ui自定义组件 创建自定义组件&#xff0c;组件名称根据你的需求来&#xff0c;最好一个组件一个文件夹&#xff0c;下图是我的示例。 src/components 组件和你写页面一样&#xff0c;所谓组件就是方便实用&#xff0c;不…

NotePad++ 在行前/行后添加特殊字符内容方法

我们在处理数据时&#xff0c;会遇到需要在每行数据前面、后面、开头、结尾添加各种不一样的字符 如果数据不多&#xff0c;我们可以自己手动的去添加&#xff0c;但如果达到了成百上千行&#xff0c;此时再机械的手动添加是不现实的 这里教给大家如何快速的在数据每行的前后…

华为云云耀云服务器L实例评测|cento7.9在线使用cloudShell下载rpm解压包安装mysql并开启远程访问

文章目录 ⭐前言⭐使用华为cloudShell连接远程服务器&#x1f496; 进入华为云耀服务器控制台&#x1f496; 选择cloudShell ⭐安装mysql压缩包&#x1f496; wget下载&#x1f496; tar解压&#x1f496; 安装步骤&#x1f496; 初始化数据库&#x1f496; 修改密码&#x1f4…

JavaCTF记录

Springmvcdemo 在没有提升权限之前&#xff0c;整个环境只有Cookie是可控的&#xff0c;并且提升权限也是要通过cookie来&#xff0c;先看看它对cookie做了什么&#xff0c;看一下过滤器 public void doFilter(ServletRequest request, ServletResponse response, FilterChai…

Python实现猎人猎物优化算法(HPO)优化随机森林回归模型(RandomForestRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 猎人猎物优化搜索算法(Hunter–prey optimizer, HPO)是由Naruei& Keynia于2022年提出的一种最新的…

Pyhton压缩JS代码

文章目录 1.安装依赖2.目录结构3.代码4.执行结果 1.安装依赖 pip install jsmin2.目录结构 3.代码 import jsmindef run(src_path, tgt_path):with open(src_path, "r", encodingutf-8) as input_file:with open(tgt_path, "w", encodingutf-8) as outpu…

外贸型CRM软件系统的作用

外贸企业在国际市场上面临着大量的竞争和风险&#xff0c;需要不断创新发展&#xff0c;提高自身的竞争力&#xff0c;但又受制于客户管理、业务效率、数据利用和风险控制等方面的不足。为了解决外贸企业面临的问题和挑战&#xff0c;外贸CRM系统应运而生。那么&#xff0c;什么…

面试(架构,网络)

java八股 treemap和linkdedhashmap区别&#xff0c;实现原理 https://blog.csdn.net/shidebin/article/details/126814905 架构 https://www.cnblogs.com/crazymakercircle/p/17197091.htmlhttps://www.cnblogs.com/crazymakercircle/p/17197091.html 羊了个羊https://www.c…

【LeetCode-简单题】1047. 删除字符串中的所有相邻重复项

文章目录 题目方法一&#xff1a;利用栈做匹配方法二&#xff1a;消消乐 题目 方法一&#xff1a;利用栈做匹配 class Solution {public String removeDuplicates(String s) {Deque<Character> deque new LinkedList<>();StringBuffer str new StringBuffer();fo…

封装七牛云存储工具类

文章目录 封装七牛云存储工具类&#xff08;为啥选择七牛云&#xff1f;当然是因为它能免费使用喽&#xff01;&#xff01;&#xff01;白嫖怪哈哈哈&#xff01;&#xff01;&#xff01;&#xff09;图片存储方案Java SDK操作七牛云封装工具类 封装七牛云存储工具类&#xf…

如何在 Excel 中求平方根

需要在 Excel 中求一个数字的平方根吗&#xff1f;使用几个内置的 Excel 函数和公式可以轻松计算平方根。在本分步指南中&#xff0c;您将学习在 Excel 中计算平方根的 5 种不同方法&#xff0c;包括使用 SQRT 函数、POWER 函数、指数公式、VBA 代码和 Power Query。跟随教程&a…

我学编程全靠B站了,真香-国外篇(第三期)

你好&#xff0c;我是Martin。 今天来点猛料&#xff0c;给大家推荐点我的压箱收藏-国外知名大学的公开课。 我推荐的不多&#xff0c;本着少就是多的原则&#xff0c;只给大家推荐我看过最好的五门视频&#xff0c;主要是来自两所国外高校&#xff1a;MIT美国麻省理工、CMU卡…

Pytorch实现MNIST字符识别

1.下载mnist.pkl.gz 网址&#xff1a;http://www.iro.umontreal.ca/~lisa/deep/data/mnist/mnist.pkl.gz 数据集文件夹路径是data2/mnist/mnist.pkl.gz 2.读取数据 from pathlib import Pathimport matplotlib.pyplot as pltDATA_PATHPath("./data2") PATHDATA_P…

无涯教程-JavaScript - LOG函数

描述 LOG函数将数字的对数返回您指定的基数。 语法 LOG (number, [base])争论 Argument描述Required/OptionalNumberThe positive real number for which you want the logarithm.RequiredBaseThe base of the logarithm. If base is omitted, it is assumed to be 10.Opti…

Linux底层基础知识

一.汇编&#xff0c;C语言&#xff0c;C&#xff0c;JAVA之间的关系 汇编&#xff0c;C语言&#xff0c;C可以通过不同的编译器&#xff0c;编译成机器码。而java只能由Java虚拟机识别。Java虚拟机可以看成一个操作系统&#xff0c;Java虚拟机是由汇编&#xff0c;C&#xff0c…