| <html devsite><head> |
| <title>模块化内核要求</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>在 Android 8.0 中,设备内核分为系统芯片 (SoC)、设备和板专属组件。基于这种分层结构的内核和 Android 使得原始设计制造商 (ODM) 和原始设备制造商 (OEM) 可以在独立的板专属树中使用板专属功能、驱动程序等,使他们可以替换通用的内核配置、以内核模块的形式添加新的驱动程序等。</p> |
| |
| <p>本页详细介绍了以下方面的要求:</p> |
| |
| <ul> |
| <li>对于独立 SoC 和 OEM/ODM 内核开发的平台支持。Android 9 建议以设备中的内核模块的形式编译和推出所有板专属代码。因此: |
| <ul> |
| <li>所有平台都应支持<a href="https://www.devicetree.org/" class="external">设备树</a>或<a href="http://www.uefi.org/acpi/specs" class="external">高级配置与电源接口 (ACPI)</a>,以便描述所有不可检测的设备。</li> |
| <li>对于基于设备树的平台,应将板专属设备节点作为<a href="/devices/architecture/dto/index.html">叠加层</a>添加到内核设备树中。</li> |
| </ul> |
| </li> |
| <li>在<a href="/devices/tech/vts/index.html">供应商测试套件 (VTS)</a> 中支持应用二进制接口 (ABI)/应用编程接口 (API) 测试,以确保指定内核可以运行 Android 开放源代码项目 (AOSP) 框架。</li> |
| <li>每个 Android 版本的最低内核版本,以及对于生成 <a href="/devices/architecture/vintf/index.html">Android 供应商接口 (VINTF) 内核对象</a>的支持。</li> |
| </ul> |
| |
| <h2 id="loadable-kernel-modules">可加载的内核模块</h2> |
| <p>所有 SoC 内核都必须支持可加载的内核模块。作为着手点,以下内核配置选项(或其内核版本等效项)已添加到所有通用内核中的 <a href="https://android.googlesource.com/kernel/common/+/android-4.4-o/android/configs/android-base.cfg" class="external">android-base.cfg</a>,且必须在所有设备内核中启用:</p> |
| |
| <pre class="prettyprint"> |
| CONFIG_MODULES=y |
| CONFIG_MODULE_UNLOAD=y |
| CONFIG_MODVERSIONS=y |
| </pre> |
| |
| <p>内核模块应尽可能支持卸载和重新加载。</p> |
| |
| <aside class="note"><strong>注意</strong>:<code>CONFIG_MODULE_SRCVERSION_ALL</code> 是可选项,不会对其进行测试。</aside> |
| |
| <h3 id="module-signing">模块签名</h3> |
| <p>(可选)ODM 可以启用以下内核配置选项,以在其自己的内核配置中启用模块签名:</p> |
| |
| <pre class="prettyprint"> |
| CONFIG_MODULE_SIG=y |
| CONFIG_MODULE_SIG_FORCE=y |
| </pre> |
| |
| <p>在需要支持验证启动的设备上,Android 要求内核模块位于启用 dm-verity 的分区中。模块签名并非强制性要求,也不会进行测试;不过,如有需要,ODM 也可以启用模块签名,但前提是 ODM 拥有所需的密钥签名及其他基础架构,以确保未来可进行独立的内核和文件系统 OTA 更新。</p> |
| |
| <h3 id="file-locations">文件位置</h3> |
| <p>Android 7.x 及更早版本对内核模块(包括对 <code>insmod</code> 和 <code>rmmod</code> 的支持)没有强制要求,而 Android 8.x 及更高版本建议在生态系统中使用内核模块。下表显示了 Android 的 3 种启动模式所需的潜在板专用外设支持:</p> |
| |
| <table> |
| <tbody><tr> |
| <th>启动模式</th> |
| <th>存储</th> |
| <th>显示</th> |
| <th>拨号键盘</th> |
| <th>电池</th> |
| <th>PMIC</th> |
| <th>触摸屏</th> |
| <th>NFC、WLAN、<br />蓝牙</th> |
| <th>传感器</th> |
| <th>相机</th> |
| </tr> |
| <tr> |
| <td>恢复</td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-no"></span></td> |
| <td><span class="compare-no"></span></td> |
| <td><span class="compare-no"></span></td> |
| <td><span class="compare-no"></span></td> |
| </tr> |
| <tr> |
| <td>充电</td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-no"></span></td> |
| <td><span class="compare-no"></span></td> |
| <td><span class="compare-no"></span></td> |
| <td><span class="compare-no"></span></td> |
| </tr> |
| <tr> |
| <td>Android</td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| <td><span class="compare-yes"></span></td> |
| </tr> |
| </tbody></table> |
| |
| <p>除了按 Android 启动模式的可用情况对内核模块进行分类之外,还可以按照所有者(SoC 供应商或 ODM)进行分类。如果使用了内核模块,则它们在文件系统中的放置位置的要求如下:</p> |
| |
| <ul> |
| <li>所有内核都应内置对启动和装载分区的支持。 |
| </li> |
| <li>必须从只读分区加载内核模块。</li> |
| <li>对于需要支持验证启动的设备,应从验证分区加载内核模块。</li> |
| <li>内核模块不应位于 <code>/system</code> 中。</li> |
| <li>完整 Android 模式或充电模式所需的 SoC 供应商内核模块应该位于 <code>/vendor/lib/modules</code> 中。</li> |
| <li>如果存在 ODM 分区,则完整 Android 模式或充电模式所需的 ODM 内核模块应该位于 <code>/odm/lib/modules</code> 中。如果不存在,则这些模块应该位于 <code>/vendor/lib/modules</code> 中。</li> |
| <li>恢复模式所需的 SoC 供应商和 ODM 的内核模块应该位于 <code>/lib/modules</code> 下的恢复 <code>ramfs</code> 中。</li> |
| <li>如果恢复模式和完整 Android 模式/充电模式都需要某个内核模块,则该模块应同时位于恢复 <code>rootfs</code> 和 <code>/vendor</code> 或 <code>/odm<strong> </strong></code> 分区中(如上所述)。</li> |
| <li>恢复模式所用的内核模块不应依赖仅位于 <code>/vendor</code> 或 <code>/odm</code> 中的模块,因为这些分区在恢复模式下没有装载。</li> |
| <li>SoC 供应商内核模块不应依赖 ODM 内核模块。</li> |
| </ul> |
| |
| <p>在 Android 7.x 及更早版本中,<code>/vendor</code> 和 <code>/odm</code> 分区<strong>不会</strong>提前装载。在 Android 8.x 及更高版本中,为使模块能够从这些分区加载,已进行相关配置,以便为<a href="/devices/tech/ota/ab_updates">非 A/B 和 A/B 设备</a>提前装载分区。这还确保了在 Android 和充电模式下均装载分区。</p> |
| |
| <h3 id="android-build-system-support">Android 编译系统支持</h3> |
| <p>在 <code>BoardConfig.mk</code> 中,Android 编译系统定义了 <code>BOARD_VENDOR_KERNEL_MODULES</code> 变量,该变量提供了用于供应商映像的内核模块的完整列表。此变量中列出的模块会被复制到位于 <code>/lib/modules/</code> 的供应商映像中,在 Android 中装载后会显示在 <code>/vendor/lib/modules</code> 中(根据上述要求)。下面是一个供应商内核模块的配置示例:</p> |
| |
| <pre class="prettyprint"> |
| vendor_lkm_dir := device/$(vendor)/lkm-4.x |
| BOARD_VENDOR_KERNEL_MODULES := \ |
| $(vendor_lkm_dir)/vendor_module_a.ko \ |
| $(vendor_lkm_dir)/vendor_module_b.ko \ |
| $(vendor_lkm_dir)/vendor_module_c.ko |
| </pre> |
| |
| <p>…其中,供应商内核模块预构建代码库会映射到 Android 编译系统中的上述位置。</p> |
| |
| <p>恢复映像可能包含供应商模块的子集。Android 编译系统定义了这些模块的变量 <code>BOARD_RECOVERY_KERNEL_MODULES</code>。例如:</p> |
| |
| <pre class="prettyprint"> |
| vendor_lkm_dir := device/$(vendor)/lkm-4.x |
| BOARD_RECOVERY_KERNEL_MODULES := \ |
| $(vendor_lkm_dir)/vendor_module_a.ko \ |
| $(vendor_lkm_dir)/vendor_module_b.ko |
| </pre> |
| |
| <p>Android 编译系统负责运行 <code>depmod</code> 以在 <code>/vendor/lib/modules</code> 和 <code>/lib/modules</code> (<code>recovery ramfs</code>) 中生成所需的 <code>modules.dep</code> 文件。</p> |
| |
| <h3 id="module-loading-&-versioning">模块加载和版本管理</h3> |
| <p>我们建议通过调用 <code>modprobe -a</code> 从 <code>init.rc*</code> 一次加载所有内核模块。这样可以避免重复初始化 <code>modprobe</code> 二进制文件的 C 运行时环境产生的开销。您可以修改 <code>early-init</code> 事件来调用 <code>modprobe</code>:</p> |
| |
| <pre class="prettyprint"> |
| on early-init |
| exec u:r:modprobe:s0 -- /vendor/bin/modprobe -a -d \ |
| /vendor/lib/modules module_a module_b module_c ... |
| </pre> |
| |
| <p>通常,内核模块必须使用将与该模块结合使用的内核进行编译,否则,内核会拒绝加载该模块。 |
| <code>CONFIG_MODVERSIONS</code> 通过检测 ABI 中的损坏情况提供了一种解决方案。该功能会计算内核中每个导出的符号的原型的循环冗余校验 (CRC) 值,并将这些值作为内核的一部分进行存储;对于内核模块所用的符号,相应的值也会存储在内核模块中。模块加载完成后,模块所用符号的值将与内核中的相应值进行比较。如果这些值相互匹配,则加载模块;如果不匹配,则模块加载会失败。</p> |
| |
| <p>要使内核映像的更新独立于供应商映像,请启用 <code>CONFIG_MODVERSIONS</code>。这样做可以在确保与供应商映像中的现有内核模块保持兼容的同时,对内核进行小幅度更新(例如 LTS 提供的问题修复)。不过,<code>CONFIG_MODVERSIONS</code> 本身并不会修复 ABI 损坏。如果内核中某个导出的符号的原型由于源代码的修改或内核配置更改而发生变化,则会破坏与使用该符号的内核模块的兼容性。在此类情况下,必须重新编译内核模块。</p> |
| |
| <p>例如,内核中的 <code>task_struct</code> 结构(在 <code>include/linux/sched.h</code> 中定义)包含很多字段,具体包含的字段根据相关条件取决于内核配置。<code>sched_info</code> 字段仅在 <code>CONFIG_SCHED_INFO</code> 启用(启用 <code>CONFIG_SCHEDSTATS</code> 或 <code>CONFIG_TASK_DELAY_ACCT</code> 时发生)时才显示。如果这些配置选项的状态发生变化,<code>task_struct</code> 结构的布局也将发生变化,同时,从内核导出的使用 <code>task_struct</code> 的所有接口都会发生变化(如 <code>kernel/sched/core.c</code> 中的 <code>set_cpus_allowed_ptr</code>)。与使用这些接口的之前编译的内核模块的兼容性将会被破坏,这就需要使用新的内核配置重新编译这些模块。</p> |
| |
| <p>要详细了解 <code>CONFIG_MODVERSIONS</code>,请参阅位于 <code>Documentation/kbuild/modules.txt</code> 的内核树中的相关文档。</p> |
| |
| <h2 id="mounting-partitions-early-first-stage-mount">提前装载分区(第一阶段装载)</h2> |
| <font style="font-family: Roboto, Arial, Helvetica, sans-serif; background-color: green; color: white"> 必须执行的工作 </font> |
| <p>所有支持 Treble 的设备都必须启用第一阶段装载,以确保 <code>init</code> 可以加载分布在 <code>system</code> 和 <code>vendor</code> 分区的 SELinux 政策片段(这样还可实现在内核启动后尽快加载内核模块)。</p> |
| |
| <aside class="note"><strong>注意</strong>:要详细了解 Android 8.x 中的 SELinux,请参阅 <a href="/security/selinux/images/SELinux_Treble.pdf">SELinux for Android 8.0</a>。</aside> |
| |
| <p>Android 必须有权访问模块所在的文件系统。为此,Android 8.x 及更高版本支持在 <code>init</code> 的第一阶段(即初始化 SELinux 之前)装载 <code>/system</code>、<code>/vendor</code> 或 <code>/odm</code>。设备制造商可以使用<a href="/devices/architecture/dto/index.html">设备树叠加层</a>为提前装载的分区指定 <code>fstab</code> 条目。</p> |
| |
| <aside class="key-point"><strong>AOSP 提前装载更改摘要:</strong> |
| <ul> |
| <li> |
| <a href="https://android-review.googlesource.com/#/q/topic:pre-early-mount" class="external">提前装载 v1</a> 和 <a href="https://android-review.googlesource.com/#/q/status:merged+project:platform/system/core+branch:master+topic:pre-early-mount-2" class="external">v2</a></li> |
| <li><a href="https://android-review.googlesource.com/#/c/339471/" class="external">杂项分区查找移除</a></li> |
| <li> |
| <a href="https://android-review.googlesource.com/#/q/branch:master+topic:early-mount-support" class="external">提前装载支持</a></li> |
| <li> |
| <a href="https://android-review.googlesource.com/#/q/branch:master+topic:early-mount-support" class="external">使用 VBoot 2.0 (AVB) 完成提前装载</a></li> |
| </ul> |
| </aside> |
| |
| <h3 id="early-mounting-partitions-vboot-1-0">提前装载分区 (VBoot 1.0)</h3> |
| <p>使用 VBoot 1.0 提前装载分区的要求包括:</p> |
| <ol> |
| <li>设备节点路径必须在 <code>fstab</code> 和设备树条目中使用其 by-name 符号链接。<em></em>例如,确保对分区进行命名且设备节点为 <code>/dev/block/…./by-name/{system,vendor,odm}</code>,而不是使用 <code>/dev/block/mmcblk0pX</code> 指定分区。 |
| </li> |
| <li>在产品的设备配置中(即 <code>device/<em>oem</em>/<em>project</em>/device.mk</code> 中)为 <code>PRODUCT_{SYSTEM,VENDOR}_VERITY_PARTITION</code> 和 <code>CUSTOM_IMAGE_VERITY_BLOCK_DEVICE</code> 指定的路径必须与 <code>fstab</code> 设备树条目中相应块设备节点指定的 by-name 相匹配。<em></em>例如: |
| <pre class="prettyprint"> |
| PRODUCT_SYSTEM_VERITY_PARTITION := /dev/block/…./by-name/system |
| PRODUCT_VENDOR_VERITY_PARTITION := /dev/block/…./by-name/vendor |
| CUSTOM_IMAGE_VERITY_BLOCK_DEVICE := /dev/block/…./by-name/odm |
| </pre> |
| </li> |
| <li>通过设备树叠加层提供的条目不得在 <code>fstab</code> 文件片段中出现重复。例如,指定某个条目以在设备树中装载 <code>/vendor</code> 时,<code>fstab</code> 文件不得重复该条目。</li> |
| <li><strong>不得</strong>提前装载需要 <code>verifyatboot</code> 的分区(此操作不受支持)。</li> |
| <li>必须在内核命令行中使用 <code>androidboot.veritymode</code> 选项指定验证分区的真实模式/状态(现有要求)。</li> |
| </ol> |
| |
| <h3 id="early-mounting-device-tree-vboot-1-0">提前装载设备树 (VBoot 1.0)</h3> |
| <p>在 Android 8.x 及更高版本中,<code>init</code> 会解析设备树并创建 <code>fstab</code> 条目,以在其第一阶段提前装载分区。<code>fstab</code> 条目采取以下形式:</p> |
| |
| <pre class="prettyprint">src mnt_point type mnt_flags fs_mgr_flags</pre> |
| |
| <p>定义设备树属性以模拟该格式:</p> |
| |
| <ul> |
| <li><code>fstab</code> 条目必须在设备树中的 <code>/firmware/android/fstab</code> 下,且必须将兼容字符串集设为 <code>android,fstab</code>。</li> |
| <li><code>/firmware/android/fstab</code> 下的每个节点都被视为单个提前装载 <code>fstab</code> 条目。节点必须定义以下属性: |
| <ul> |
| <li><code>dev</code>。必须指向代表 by-name 分区的设备节点。<em></em></li> |
| <li><code>type</code>。必须是文件系统类型(如在 <code>fstab</code> 文件中一样)。</li> |
| <li><code>mnt_flags</code>。必须是装载标记的逗号分隔列表(如在 <code>fstab</code> 文件中一样)。</li> |
| <li><code>fsmgr_flags</code>。必须是 Android <code>fs_mgr |
| flags</code> 列表(如在 <code>fstab</code> 文件中一样)。 |
| <ul> |
| <li>A/B 分区必须具有 <code>slotselect fs_mgr</code> 选项。</li> |
| <li>已启用 dm-verity 的分区必须具有 <code>verify fs_mgr</code> 选项。 |
| </li> |
| </ul> |
| </li> |
| </ul> |
| </li> |
| </ul> |
| |
| <h4><strong>示例</strong>:N6P 上的 /system 和 /vendor</h4> |
| <p>下面的示例显示的是在 Nexus 6P 上为 <code>system</code> 和 <code>vendor</code> 分区提前装载设备树:</p> |
| |
| <pre class="prettyprint"> |
| / { |
| firmware { |
| android { |
| compatible = "android,firmware"; |
| fstab { |
| compatible = "android,fstab"; |
| system { |
| compatible = "android,system"; |
| dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/system"; |
| type = "ext4"; |
| mnt_flags = "ro,barrier=1,inode_readahead_blks=8"; |
| fsmgr_flags = "wait,verify"; |
| }; |
| vendor { |
| compatible = "android,vendor"; |
| dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor"; |
| type = "ext4"; |
| mnt_flags = "ro,barrier=1,inode_readahead_blks=8"; |
| fsmgr_flags = "wait"; |
| }; |
| }; |
| }; |
| }; |
| }; |
| </pre> |
| |
| <h4><strong>示例</strong>:Pixel 上的 /vendor</h4> |
| <p>下面的示例显示的是在 Pixel 上为 <code>/vendor</code> 提前装载设备树(请务必为 A/B 分区添加 <code>slotselect</code>):</p> |
| |
| <pre class="prettyprint"> |
| / { |
| firmware { |
| android { |
| compatible = "android,firmware"; |
| fstab { |
| compatible = "android,fstab"; |
| vendor { |
| compatible = "android,vendor"; |
| dev = "/dev/block/platform/soc/624000.ufshc/by-name/vendor"; |
| type = "ext4"; |
| mnt_flags = "ro,barrier=1,discard"; |
| fsmgr_flags = "wait,slotselect,verify"; |
| }; |
| }; |
| }; |
| }; |
| }; |
| </pre> |
| |
| <h3 id="early-mounting-partitions-vboot-2-0">提前装载分区 (VBoot 2.0)</h3> |
| <p>VBoot 2.0 是 <a href="https://android.googlesource.com/platform/external/avb/" class="external">Android 验证启动 (AVB)</a>。 使用 VBoot 2.0 提前装载分区的要求如下:</p> |
| <ol> |
| <li>设备节点路径必须在 <code>fstab</code> 和设备树条目中使用其 by-name 符号链接。<em></em>例如,确保对分区进行命名且设备节点为 <code>/dev/block/…./by-name/{system,vendor,odm}</code>,而不是使用 <code>/dev/block/mmcblk0pX</code> 指定分区。</li> |
| <li>VBoot 1.0 所用的编译系统变量(如 <code>PRODUCT_{SYSTEM,VENDOR}_VERITY_PARTITION</code> 和 <code>CUSTOM_IMAGE_VERITY_BLOCK_DEVICE</code>)对 VBoot 2.0 而言并非必需的。相反,您应该定义 VBoot 2.0 中引入的新编译系统变量(包括 <code>BOARD_AVB_ENABLE := true</code>);有关完整配置,请参阅<a href="https://android.googlesource.com/platform/external/avb/#Build-System-Integration" class="external">适用于 AVB 的编译系统集成</a>。</li> |
| <li>通过设备树叠加层提供的条目不得在 <code>fstab</code> 文件片段中出现重复。例如,如果您指定某个条目以在设备树中装载 <code>/vendor</code>,则 <code>fstab</code> 文件不得重复该条目。</li> |
| <li>VBoot 2.0 不支持 <code>verifyatboot</code>,无论是否启用了提前装载。</li> |
| <li>必须在内核命令行中使用 <code>androidboot.veritymode</code> 选项指定验证分区的真实模式/状态(现有要求)。 |
| 确保包含以下 AVB 修复程序: |
| <ul> |
| <li> |
| <a href="https://android-review.googlesource.com/#/q/topic:libavb-api-rev-for-verity-modes+(status:open+OR+status:merged)" class="external">https://android-review.googlesource.com/#/q/topic:libavb-api-rev-for-verity-modes+(status:open+OR+status:merged)</a></li> |
| <li> |
| <a href="https://android-review.googlesource.com/#/c/394215/" class="external">https://android-review.googlesource.com/#/c/394215/</a></li> |
| </ul> |
| </li> |
| </ol> |
| |
| <h3 id="early-mounting-device-tree-vboot-2-0">提前装载设备树 (VBoot 2.0)</h3> |
| <p>VBoot 2.0 设备树中的配置与 <a href="#early-mounting-device-tree-vboot-1-0">VBoot 1.0</a> 中的大致相同,但还有以下几项不同之处:</p> |
| <ul> |
| <li><code>fsmgr_flag</code> 由 <code>verify</code> 变为 <code>avb</code>。</li> |
| <li>包含 AVB 元数据的所有分区都必须位于设备树的 vbmeta 条目中,即使相应的分区并非提前装载的分区(如 <code>/boot</code>)也是如此。</li> |
| </ul> |
| |
| <h4><strong>示例</strong>:N5X 上的 /system 和 /vendor</h4> |
| <p>下面的示例显示的是在 Nexus 5X 上为 <code>system</code> 和 <code>vendor</code> 分区提前装载设备树。请注意:</p> |
| <ul> |
| <li><code>/system</code> 使用 AVB 进行装载,且 <code>/vendor</code> 的装载不需要进行完整性验证。</li> |
| <li>由于 Nexus 5X 没有 <code>/vbmeta</code> 分区,因此顶级 vbmeta 位于 <code>/boot</code> 分区的末端(有关详情,请参阅 <a href="https://android-review.googlesource.com/#/c/344907/" class="external">AOSP 变更列表</a>)。 |
| |
| <pre class="prettyprint"> |
| / { |
| firmware { |
| android { |
| compatible = "android,firmware"; |
| vbmeta { |
| compatible = "android,vbmeta"; |
| parts = "boot,system,vendor"; |
| }; |
| fstab { |
| compatible = "android,fstab"; |
| system { |
| compatible = "android,system"; |
| dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/system"; |
| type = "ext4"; |
| mnt_flags = "ro,barrier=1,inode_readahead_blks=8"; |
| fsmgr_flags = "wait,avb"; |
| }; |
| vendor { |
| compatible = "android,vendor"; |
| dev = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor"; |
| type = "ext4"; |
| mnt_flags = "ro,barrier=1,inode_readahead_blks=8"; |
| fsmgr_flags = "wait"; |
| }; |
| }; |
| }; |
| }; |
| }; |
| </pre> |
| </li> |
| </ul> |
| |
| <h4><strong>示例</strong>:Pixel 上的 /vendor</h4> |
| <p>下面的示例显示的是在 Pixel 上提前装载 <code>/vendor</code>。 |
| 请注意:</p> |
| |
| <ul> |
| <li>很多分区都是在 vbmeta 条目中指定的,因为这些分区<a href="https://android.googlesource.com/platform/external/avb/#The-VBMeta-struct">受 AVB 保护</a>。</li> |
| <li>请务必包含所有 AVB 分区,即使仅提前装载了 <code>/vendor</code> 也是如此。</li> |
| <li>请务必为 A/B 分区添加 <strong><code>slotselect</code></strong>。 |
| <pre class="prettyprint"> |
| / { |
| vbmeta { |
| compatible = "android,vbmeta"; |
| parts = "vbmeta,boot,system,vendor,dtbo"; |
| }; |
| firmware { |
| android { |
| compatible = "android,firmware"; |
| fstab { |
| compatible = "android,fstab"; |
| vendor { |
| compatible = "android,vendor"; |
| dev = "/dev/block/platform/soc/624000.ufshc/by-name/vendor"; |
| type = "ext4"; |
| mnt_flags = "ro,barrier=1,discard"; |
| fsmgr_flags = "wait,slotselect,avb"; |
| }; |
| }; |
| }; |
| }; |
| }; |
| </pre> |
| </li> |
| </ul> |
| |
| <h2 id="device-tree-overlay-support-bootloader">设备树叠加层支持(引导加载程序)</h2> |
| <p> |
| <a href="/devices/architecture/dto/">设备树叠加层</a> (DTO) 旨在扩展现有<a href="https://events.linuxfoundation.org/sites/events/files/slides/petazzoni-device-tree-dummies.pdf" class="external">扁平化设备树 (FDT)</a> 的实现,以便用户空间(在运行时)可通过加载修改了原始数据的额外叠加层 FDT 来修改内核中的初始设备树数据。Android 不需要在运行时更新用户空间的 DT Blob,而是建议供应商借助 <code>libfdt</code>/<code>libufdt</code> 在引导加载程序中添加设备树补丁程序。</p> |
| |
| <p>Android 对 DTO 的支持因 Android 版本而异:</p> |
| <ul> |
| <li>Android 7.x 及更早版本不要求提供设备树支持,同时,没有针对供应商如何将 DT Blob 传递给内核或在何处存储这些 Blob 提供建议。</li> |
| <li>不过,Android 8.x 建议提供此类支持,以将内核的板专属部分和仅限 SoC 访问的部分区分开来。</li> |
| <li>Android 9 要求必须存在 DTBO 分区并且至少应用一个 DTO。</li> |
| </ul> |
| |
| <h3 id="partitioning-requirements">分区要求</h3> |
| <p>目前,大多数 Android 设备都在编译时将 DT Blob 附加到内核中,而引导加载程序知道如何从内核读取 DT Blob。由于 Android 对如何编译/存储 DT Blob(被视为 SoC 内核的一部分)没有特定要求,因此 DT Blob 可以附加到内核中,也可以将其单独存储在某个分区中。这里唯一的假设前提是引导加载程序已经知道如何加载以及从何处加载 DT Blob。</p> |
| |
| <p>设备树叠加层支持(如果使用)的要求如下:</p> |
| <ul> |
| <li>对于适用于板专属 DT 叠加层的每个内核映像,设备应该具有新的设备树 Blob 叠加层 (DTBO) 分区(要详细了解分区格式,请参阅 <a href="/devices/architecture/dto/partitions.html">DTB/DTBO 分区</a>)。假设的前提是引导加载程序已经知道从何处以及如何加载 SoC 专用 DTB。</li> |
| <li>应该针对 A/B 设备对叠加层 DT 分区进行 <a href="/devices/tech/ota/ab_updates.html">A/B 系统更新</a>。对这些设备而言,恢复内核与 Android 内核相同,但分区必须进行 A/B 系统更新,因为这样才可以通过 OTA 进行更新。</li> |
| <li>分区大小取决于板。 |
| <ul> |
| <li>DT 叠加层分区大小取决于设备以及主 SoC 内核 DT Blob 上所需的更改量。</li> |
| <li>DTBO 分区的大小是编译 SoC 内核所需的更改数量对应的函数。选择分区大小时需要为未来更新留出空间(通常,8MB 大小的分区已绰绰有余)。</li> |
| </ul> |
| </li> |
| </ul> |
| |
| <h3 id="bootloader-requirements">引导加载程序要求</h3> |
| <p>引导加载程序的要求包括:</p> |
| <ul> |
| <li>引导加载程序应该知道如何以及从何处(考虑使用 A/B 设备的启动槽)以供应商独有的方式加载 SoC 专用 DT Blob。这通常是从内核映像的末端提取,因为 Blob 已附加到内核中。</li> |
| <li>引导加载程序应该知道如何以及从何处(考虑使用 A/B 设备的启动槽)以供应商独有的方式加载叠加层 DT Blob。</li> |
| <li>将组合设备树传递给内核之前,引导加载程序必须使用叠加层修补主 DT Blob。</li> |
| </ul> |
| |
| <p>要详细了解如何在引导加载程序中增加对 DTO 的支持,请参阅<a href="/devices/architecture/dto/index.html">设备树叠加层</a>。</p> |
| |
| <h2 id="core-kernel-requirements">核心内核要求</h2> |
| <p>自 Android 8.0 开始,Android 规定了最低内核版本和内核配置,且会在 VTS 中以及 OTA 期间对它们进行检查。Android 设备内核必须启用内核 <code>.config</code> 支持以及在运行时通过 <code>procfs</code> 读取内核配置的选项。</p> |
| |
| <h3 id="kernel-config-support">内核 .config 支持</h3> |
| <p>所有设备内核都必须完整启用 <a href="https://android.googlesource.com/kernel/common/+/android-4.4/android/configs/android-base.cfg" class="external">android-base.cfg</a>,其中必须包含以下内核配置选项(或其内核版本等效选项):</p> |
| |
| <pre class="prettyprint"> |
| CONFIG_IKCONFIG=y |
| CONFIG_IKCONFIG_PROC=y |
| </pre> |
| |
| <h3 id="kernel-version">内核版本</h3> |
| <p>对于 Android 9,要求的最低 LTS 内核版本是 4.4.107、4.9.84 和 4.14.42。</p> |
| |
| <ul> |
| <li>2018 年构建的所有 SoC 都必须使用 4.9.84 版或更新版本的内核才能发布。</li> |
| <li>在推出搭载 Android 8.x 的新 Android 设备时,所有其他 SoC 都必须使用 3.18 版或更新版本的内核。在推出搭载 Android 9 的新 Android 设备时,所有其他 SoC 都必须使用 4.4.107 版或更新版本的内核。</li> |
| <li>基于 4.14 的设备内核必须包含 4.14.42 或更高版本的 LTS。 |
| </li> |
| <li>发布搭载 Android 8.x 及更高版本的设备时,所有 SoC 仍然需要遵循启用 Treble 所需的内核更改要求,无论发布日期是什么时候。</li> |
| <li>如有需要,将升级到 Android 8.x 或 9 的早期 Android 设备可以继续使用原来的基础内核版本。</li> |
| </ul> |
| |
| <p>要详细了解 LTS 内核,请参阅<a href="/devices/architecture/kernel/releases#long-term-stable-kernels">长期稳定的内核</a>和 <a href="https://source.android.com/devices/architecture/kernel/android-common">Android 通用内核</a></p> |
| |
| <h3 id="device-tree-support">设备树支持</h3> |
| <p>内核中必须启用设备树支持,且引导加载程序必须将硬件描述以设备树的形式传递给内核(除非平台支持 ACPI)。设备树还必须可供 Android 读取,且能够将供应商/ODM 特有的参数传递给 Android。 |
| <code>CONFIG_OF</code>(以及所有其他设备/子系统专用的 <code>CONFIG_OF_*</code> 内核配置选项)是强制性的选项。</p> |
| |
| <p>3.15 版之前的内核需要提供 <code>CONFIG_PROC_DEVICETREE</code>,以便 Android 在启动初期就能访问供应商/ODM 特有的配置。在 3.15 版及更高版本的内核中,该选项的功能已并入 <code>CONFIG_OF</code> 中。</p> |
| |
| <pre class="prettyprint"> |
| CONFIG_OF=y |
| CONFIG_PROC_DEVICETREE=y (kernels prior to 3.15) |
| </pre> |
| |
| <p>要查看使用设备树提前装载 <code>vendor</code>/<code>odm</code> 分区的示例,请参阅 <a href="https://android-review.googlesource.com/#/c/337310/" class="external">AOSP 变更列表</a>。</p> |
| |
| <h3 id="debugfs">DebugFS</h3> |
| <p>供应商接口的实现不应依赖 <code>debugfs</code>,您可以启用 debugfs,但在不装载 <code>debugfs</code> 的情况下也可以完成 VTS 测试。</p> |
| |
| <h2 id="beyond-android-o">未来的 Android 版本</h2> |
| <p>当前的 Android 版本建议以设备中的内核模块的形式编译和推出所有板专属代码。对 Android 而言,内核的其余部分为一个整体(无论它实际上是单片内核,还是其中的一部分是作为内核模块编译的)。</p> |
| |
| <p>该单片内核是可以在 SoC 供应商的参考硬件上启动的 SoC 内核,但仅限于此。如今,对 SoC 内核的处理方式与通用内核类似;SoC 内核在板专属的代码库中会有大量副本。这种分发模型会导致,针对每个分支中的同一错误,系统会采取极为不同的方式修复 SoC 内核;这样一来,由于会在不同的时间择优挑选或修复同一错误的方式不同,未来的内核更新会有延迟。要解决此问题,必须单独提供 SoC 内核,以便使用 SoC 的每个人都可以为同一 SoC 内核做贡献。</p> |
| |
| <p>图 1(下图)是一个常见示例,显示了 SoC 内核如何随着时间的推移在各个 Android 版本以及 ODM 之间逐渐碎片化。</p> |
| |
| <img src="../images/treble_kernel_current.png"/> |
| <figcaption><strong>图 1.</strong> 设备内核副本</figcaption> |
| |
| <p>图 1 表明:</p> |
| <ol> |
| <li>每个人都需要花费大量的时间和精力在板专属分支/标签之间进行交叉合并。</li> |
| <li>等待交叉合并的同时,Android 设备制造商会修补他们自己的内核以获取错误/安全漏洞修复程序。</li> |
| <li>与父级的偏离导致未来的升级/合并变得非常困难。</li> |
| </ol> |
| <p>针对通用 SoC 内核提议的模型可解决上行合并更改(如 SoC 专属错误修复程序、LTS 升级、安全漏洞修复程序等)导致的问题。 |
| 图 2(下图)显示了工作流程在按 SoC 和内核进行过统一的理想场景中如何变化:</p> |
| |
| <img src="../images/treble_kernel_treble.png"/> |
| <figcaption><strong>图 2.</strong> Android 8.x 及更高版本的设备内核</figcaption> |
| |
| <p>这种方法旨在通过推荐并与设备制造商展开协作以采用最新的通用 SoC 内核,解决内核代码库碎片化的问题。Android 8.x 及更高版本为 ODM 提供了各种可能的选项,使 ODM 不需要维护自己的 SoC 内核,而是依赖通用的 SoC 内核来获取 LTS 升级/问题修复程序/安全漏洞补丁程序等。</p> |
| |
| <p>我们初步的计划是推动所有 ODM/供应商都使用单一的 SoC 内核源。未来,我们计划朝着每个 SoC 分发单个内核二进制文件的方向发展。</p> |
| |
| <h3 id="upstreaming">上游化</h3> |
| <p>为了更轻松且近乎自动地更新为较新的内核版本,并为 ODM 提供更安全可靠的平台来开发产品,我们强烈建议 SoC 供应商将其内核更改上游化,并使它们被 kernel.org 主代码库接受。虽然这样做需要前期投入额外的时间和工程资源,但事实证明,从长远的角度来看,这样会节省时间和资金。另据证实,与未经社区审核的代码相比,合并后的代码质量高很多,错误和安全漏洞问题也更少(这些错误和安全漏洞问题的严重程度通常更低)。</p> |
| |
| <p>如果将对 SoC 的全面支持合并到上游,社区便可以在内部的内核 API 随时间不断发展的同时,做出必要的 API 更改,从而自动延长平台的使用寿命。通过将硬件平台添加到诸多社区管理的内核测试平台的其中一个(如 <code><a href="https://kernelci.org" class="external">kernelci.org</a></code>),还可以在开发和稳定版本中针对任何回归自动测试内核。 |
| </p> |
| |
| <p>要获取与 Linux 内核社区协作以将您的代码上游化的相关帮助,请参阅以下资源:</p> |
| <ul> |
| <li><code>Documentation/process</code>(在 4.9 版及更早版本中为 <code>Documentation/development-process</code>)</li> |
| <li><code>Documentation/CodingStyle</code></li> |
| <li><code>Documentation/SubmittingPatches</code></li> |
| </ul> |
| |
| <p>社区会稍作审核就接受独立的驱动程序和文件系统,并将其纳入内核的暂存区,然后在其中努力提高代码质量。</p> |
| |
| </body></html> |