blob: 5e097d779dd1f21988a8e1743bc732cd59ea604c [file] [log] [blame]
<html devsite><head>
<title>优化 DTO</title>
<meta name="project_path" value="/_project.yaml"/>
<meta name="book_path" value="/_book.yaml"/>
</head>
<body>
<!--
Copyright 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<p>
本页介绍了您可以对 DTO 实现进行哪些优化,描述了针对叠加根节点的限制,并详细介绍了如何在 DTBO 映像中配置经过压缩的叠加层。此外,还提供了示例实现说明和代码。
</p>
<h2 id="kernel">内核命令行</h2>
<p>
设备树中的原始内核命令行位于 <code>chosen/bootargs</code> 节点中。引导加载程序必须将此位置与内核命令行的其他源位置进行连接:
</p>
<pre class="prettyprint">
/dts-v1/;
/ {
chosen: chosen {
bootargs = "...";
};
};
</pre>
<p>
DTO <strong>无法</strong>连接主 DT 和叠加 DT 的值,因此您必须将主 DT 的内核命令行置入 <code>chosen/bootargs</code> 中,并将叠加 DT 的内核命令行置入 <code>chosen/bootargs_ext</code> 中。接下来,引导加载程序会连接这些位置,并将结果传递给内核。
</p>
<table>
<tbody><tr>
<th width="50%">main.dts</th>
<th>overlay.dts</th>
</tr>
<tr>
<td>
<pre class="prettyprint">
/dts-v1/;
/ {
chosen: chosen {
bootargs = "...";
};
};
</pre>
</td>
<td class="alt">
<pre class="prettyprint">
/dts-v1/;
/plugin/;
&amp;chosen {
bootargs_ext = "...";
};
</pre>
</td>
</tr>
</tbody></table>
<h2 id="libufdt">libufdt</h2>
<p>
虽然最新的 <code><a href="https://github.com/dgibson/dtc/tree/master/libfdt" class="external">libfdt</a></code> 支持 DTO,但是我们建议您使用 <code>libufdt</code> 来实现 DTP(AOSP 源代码位于 <code><a href="https://android.googlesource.com/platform/system/libufdt/+/refs/heads/master" class="external">platform/system/libufdt</a></code> 下)。
<code>libufdt</code> 会从扁平化设备树 (FDT) 编译真实的树结构(非扁平化设备树,简称“ufdt”),从而改善两个 <code>.dtb</code> 文件(从 O(N2) 到 O(N),其中 N 是树中的节点数)的合并效果。
<em></em>
</p>
<h3 id="performance">性能测试</h3>
<p>
在 Google 的内部测试中,进行编译后,在 2405 个 <code>.dtb</code> 和 283 个 <code>.dtbo</code> DT 节点上使用 <code>libufdt</code> 生成了 70618 字节和 8566 字节的文件。与从 FreeBSD 移植的 <a href="http://fxr.watson.org/fxr/source/boot/fdt/" class="external">DTO 实现</a>(运行时为 124 毫秒)相比,<code>libufdt</code> DTO 运行时为 10 毫秒。
</p>
<p>
Pixel 设备的性能测试比较了 <code>libufdt</code><code>libfdt</code>。基本节点数量带来的影响相似,但包含以下差异:
</p>
<ul>
<li>500 次叠加(附加或覆盖)操作具有 6-8 倍的时间差异</li>
<li>1000 次叠加(附加或覆盖)操作具有 8-10 倍的时间差异</li>
</ul>
<p>
附加计数设置为 X 的示例:
</p>
<p><img src="../images/treble_dto_appending.png"/></p>
<figcaption><strong>图 1.</strong> 附加计数为 X</figcaption>
<p>
覆盖计数设置为 X 的示例:
</p>
<p><img src="../images/treble_dto_overriding.png"/></p>
<figcaption><strong>图 2.</strong> 覆盖计数为 X</figcaption>
<p>
<code>libufdt</code> 是用一些 <code>libfdt</code> API 和数据结构开发的。使用 <code>libufdt</code> 时,您必须包含并关联 <code>libfdt</code>(不过,您可以在代码中使用 <code>libfdt</code> API 来操作 DTB 或 DTBO)。
</p>
<h3 id="api">libufdt DTO API</h3>
<p>
<code>libufdt</code> 中适用于 DTO 的主要 API 如下:
</p>
<pre class="prettyprint">
struct fdt_header *ufdt_apply_overlay(
struct fdt_header *main_fdt_header,
size_t main_fdt_size,
void *overlay_fdt,
size_t overlay_size);
</pre>
<p>
参数 <code>main_fdt_header</code> 是主 DT,<code>overlay_fdt</code> 是包含 <code>.dtbo</code> 文件内容的缓冲区。返回值是一个包含合并的 DT 的新缓冲区(如果出现错误,则返回 <code>null</code>)。合并的 DT 采用 FDT 格式,您可以在启动内核时将其传递给内核。
</p>
<p>
来自返回值的新缓冲区由 <code>dto_malloc()</code>(您应在将 <code>libufdt</code> 移植到引导加载程序时加以实现)创建。有关参考实现,请参阅 <code>sysdeps/libufdt_sysdeps_*.c</code>
</p>
<h2 id="root">根节点限制</h2>
<p>
您不能将新节点或属性叠加到主 DT 的根节点,因为叠加操作依赖于标签。由于主 DT 必须定义一个标签,而叠加 DT 则会分配要叠加标签的节点,因此,您无法为根节点提供标签(因而不能叠加根节点)。
</p>
<p>
SoC 供应商必须定义主 DT 的叠加能力;ODM/OEM 只能使用由 SoC 供应商定义的标签附加或覆盖节点。要解决这个问题,您可以在基础 DT 中的根节点下定义一个 <strong><code>odm</code></strong> 节点,使叠加 DT 中的所有 ODM 节点都能够添加新节点。或者,您也可以将基础 DT 中的所有 SoC 相关节点放在根节点下的 <strong><code>soc</code></strong> 节点中,如下所述:
</p>
<table>
<tbody><tr>
<th width="50%">main.dts</th>
<th>overlay.dts</th>
</tr>
<tr>
<td>
<pre>
/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 */
};
};
</pre>
</td>
<td class="alt">
<pre class="prettyprint">
/dts-v1/;
/plugin/;
/ {
};
&amp;chosen {
bootargs_ex = "...";
};
&amp;odm {
odm_device@0 {
...
};
...
};
</pre>
</td>
</tr>
</tbody></table>
<h2 id="compressed-overlays">使用经过压缩的叠加层</h2>
<p>
Android 9 增加了以下支持:在使用第 1 版设备树表格表头时,在 DTBO 映像中使用经过压缩的叠加层。使用 DTBO 标头 v1 时,dt_table_entry 中标记字段的四个最不重要的有效位会指明 DT 条目的压缩格式。<em></em>
</p>
<pre class="prettyprint">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 */
};
</pre>
<p>
目前,系统支持 <code>zlib</code><code>gzip</code> 压缩。
</p>
<pre class="prettyprint">enum dt_compression_info {
NO_COMPRESSION,
ZLIB_COMPRESSION,
GZIP_COMPRESSION
};
</pre>
<p>
Android 9 增加了在 <code>VtsFirmwareDtboVerification</code> 测试中测试经过压缩的叠加层的支持,以帮助您验证叠加应用的正确性。
</p>
<h2 id="sample">DTO 实现示例</h2>
<p>
以下说明介绍了使用 <code>libufdt</code> 进行 DTO 实现的示例过程(示例代码如下)。
</p>
<h3 id="sample-instructions">示例 DTO 说明</h3>
<ol>
<li>包含库。要使用 <code>libufdt</code>,请包含 <code>libfdt</code> 以用于数据结构和 API:
<pre class="prettyprint">
#include &lt;libfdt.h&gt;
#include &lt;ufdt_overlay.h&gt;
</pre>
</li>
<li>加载主 DT 和叠加 DT。将 <code>.dtb</code><code>.dtbo</code> 从存储空间加载到内存中(确切的步骤取决于您的设计)。此时,您应该设置 <code>.dtb</code>/<code>.dtbo</code> 的缓冲区和大小:
<pre class="prettyprint">
main_size = my_load_main_dtb(main_buf, main_buf_size)
</pre>
<pre class="prettyprint">
overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
</pre>
</li>
<li>叠加 DT:
<ol>
<li>使用 <code>ufdt_install_blob()</code> 获取主 DT 的 FDT 标头:
<pre class="prettyprint">
main_fdt_header = ufdt_install_blob(main_buf, main_size);
main_fdt_size = main_size;
</pre>
</li>
<li>对 DTO 调用 <code>ufdt_apply_overlay()</code> 以获取采用 FDT 格式的合并 DT:
<pre class="prettyprint">
merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
overlay_buf, overlay_size);
</pre>
</li>
<li>使用 <code>merged_fdt</code> 获取 <code>dtc_totalsize()</code> 的大小:
<pre class="prettyprint">
merged_fdt_size = dtc_totalsize(merged_fdt);
</pre>
</li>
<li>传递合并的 DT 以启动内核:
<pre class="prettyprint">
my_kernel_entry(0, machine_type, merged_fdt);
</pre>
</li>
</ol>
</li>
</ol>
<h3 id="sample-code">示例 DTO 代码</h3>
<pre class="prettyprint">
#include &lt;libfdt.h&gt;
#include &lt;ufdt_overlay.h&gt;
{
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);
}
</pre>
</body></html>