将 fastboot 移至用户空间

Fastboot 是引导加载程序模块和模式的名称。Android 10 及更高版本通过将 fastboot 实现从引导加载程序重新定位到用户空间,来支持可调整大小的分区。这种重新定位使得可以将刷写代码移至可维护且可测试的通用位置,而只有 fastboot 的供应商特定部分通过硬件抽象层 (HAL) 实现。此外,Android 12 及更高版本支持通过添加的 fastboot 命令刷写 ramdisk。

统一 fastboot 和恢复

由于用户空间 fastboot 和恢复类似,您可以将它们合并到一个分区或二进制文件中。这样具有诸多优点,例如,占用空间更少、总分区数更少,并且 fastboot 和恢复可以共享其内核和库。

Fastbootd 是用户空间守护程序和模式的名称。为了支持 fastbootd,引导加载程序必须实现新的引导控制块 (BCB) 命令 boot-fastboot。为了进入 fastbootd 模式,引导加载程序必须将 boot-fastboot 写入 BCB 消息的命令字段,并保持 BCB 的 recovery 字段不变(以便重启任何中断的恢复任务)。statusstagereserved 字段也保持不变。引导加载程序会在 BCB 命令字段中看到 boot-fastboot 时加载并启动到恢复映像。然后,恢复会解析 BCB 消息并切换到 fastbootd 模式。

ADB 命令

本部分介绍了用于集成 fastbootdadb 命令。该命令具有不同的结果,具体取决于它是由系统还是恢复执行的。

命令 说明
reboot fastboot
  • 重启进入 fastbootd(系统)。
  • 直接进入 fastbootd 而无需重启(恢复)。

Fastboot 命令

本部分介绍了用于集成 fastbootd 的 fastboot 命令,包括用于刷写和管理逻辑分区的新命令。某些命令具有不同的结果,具体取决于它们是由引导加载程序还是 fastbootd 执行的。

命令 说明
reboot recovery
  • 重启进入恢复模式(引导加载程序)。
  • 直接进入恢复模式而无需重启 (fastbootd)。
reboot fastboot 重启进入 fastbootd
getvar is-userspace
  • 返回 yes (fastbootd)。
  • 返回 no(引导加载程序)。
getvar is-logical:<partition> 如果给定的分区是逻辑分区,则返回 yes,否则返回 no。逻辑分区支持以下列出的所有命令。
getvar super-partition-name 返回超级分区的名称。如果超级分区是 A/B 分区(通常不是),则名称包含当前槽后缀。
create-logical-partition <partition> <size> 使用给定的名称和大小创建逻辑分区。该名称不得已作为逻辑分区存在。
delete-logical-partition <partition> 删除给定的逻辑分区(实际上会擦除该分区)。
resize-logical-partition <partition> <size> 调整逻辑分区大小,新尺寸为指定大小,但不更改其内容。如果空间不足以执行调整大小操作,则会失败。
flash <partition><filename> ] 将文件写入闪存分区。设备必须处于解锁状态。
erase <partition> 擦除分区(不需要安全擦除)。设备必须处于解锁状态。
getvar <variable> | all 显示启动加载程序变量或所有变量。如果变量不存在,则返回错误。
set_active <slot>

将给定的 A/B 启动槽设置为active(活动)。在下次启动尝试时,系统将从指定的槽启动。

对于 A/B 支持,槽是分区的重复集合,可以独立启动。槽的命名方式为 ab 等,并通过在分区名称中添加后缀 _a_b 等来区分。

reboot 正常重启设备。
reboot-bootloader(或 reboot bootloader 将设备重启到启动加载程序。
fastboot fetch vendor_boot <out.img>

Android 12 及更高版本中使用此命令来支持刷写供应商 ramdisk。

获取整个分区大小和 chunk 大小。获取每个 chunk 的数据,然后将数据拼接在一起形成 <out.img>

有关详情,请参阅 fastboot fetch vendor_boot <out.img>

fastboot flash vendor_boot:default <vendor-ramdisk.img>

Android 12 及更高版本中使用此命令来支持刷写供应商 ramdisk。

这是 flash 命令的特殊变体。它执行 fetch vendor_boot 镜像功能,如同调用了 fastboot fetch 一样。它刷写的新 vendor_boot 镜像取决于启动标头版本是版本 3 还是版本 4。

有关详情,请参阅 fastboot flash vendor_boot:default <vendor-ramdisk.img>

fastboot flash vendor_boot:<foo> <vendor-ramdisk.img> Android 12 及更高版本中使用此命令来支持刷写供应商 ramdisk。

提取 vendor_boot 镜像。如果供应商启动标头是版本 3,则返回错误。如果是版本 4,它会查找正确的供应商 ramdisk 片段(如果可用)。它将该片段替换为给定的镜像,重新计算大小和偏移量,并刷写新的 vendor_boot 镜像

有关详情,请参阅 fastboot flash vendor_boot:<foo> <vendor-ramdisk.img>

Fastboot 和启动加载程序

启动加载程序刷写 bootloaderradioboot/recovery 分区,之后设备启动进入 fastboot(用户空间)并刷写所有其他分区。启动加载程序应支持以下命令。

命令 说明
download 下载要刷写的镜像。
flash recovery <image>/ flash boot <image>/ flash bootloader <image>/ 刷写 recovery/boot 分区和启动加载程序。
reboot 重启设备。
reboot fastboot 重启到 fastboot。
reboot recovery 重启到 recovery。
getvar 获取刷写 recovery/boot 镜像所需的启动加载程序变量(例如,current-slotmax-download-size)。
oem <command> OEM 定义的命令。

动态分区

启动加载程序绝不能允许刷写或擦除动态分区,如果尝试执行这些操作,必须返回错误。对于改造的动态分区设备,fastboot 工具(和启动加载程序)支持强制模式,以便在启动加载程序模式下直接刷写动态分区。例如,如果 system 是改造设备上的动态分区,则使用 fastboot --force flash system 命令会使启动加载程序(而不是 fastbootd)刷写该分区。

关机充电

如果设备支持关机充电,或者在供电时自动启动进入特殊模式,则 fastboot oem off-mode-charge 0 命令的实现必须绕过这些特殊模式,以便设备启动时如同用户按下了电源按钮一样。

Fastboot OEM HAL

要完全取代启动加载程序 fastboot,fastboot 必须处理所有现有的 fastboot 命令。这些命令中有许多来自 OEM,并且已记录在案,但需要自定义实现。许多 OEM 特定的命令记录在案。为了处理此类命令,fastboot HAL 指定了所需的 OEM 命令。OEM 也可以实现自己的命令。

fastboot HAL 的定义如下

import IFastbootLogger;

/**
 * IFastboot interface implements vendor specific fastboot commands.
 */
interface IFastboot {
    /**
     * Returns a bool indicating whether the bootloader is enforcing verified
     * boot.
     *
     * @return verifiedBootState True if the bootloader is enforcing verified
     * boot and False otherwise.
     */
    isVerifiedBootEnabled() generates (bool verifiedBootState);

    /**
     * Returns a bool indicating the off-mode-charge setting. If off-mode
     * charging is enabled, the device autoboots into a special mode when
     * power is applied.
     *
     * @return offModeChargeState True if the setting is enabled and False if
     * not.
     */
    isOffModeChargeEnabled() generates (bool offModeChargeState);

    /**
     * Returns the minimum battery voltage required for flashing in mV.
     *
     * @return batteryVoltage Minimum battery voltage (in mV) required for
     * flashing to be successful.
     */
    getBatteryVoltageFlashingThreshold() generates (int32_t batteryVoltage);

    /**
     * Returns the file system type of the partition. This is only required for
     * physical partitions that need to be wiped and reformatted.
     *
     * @return type Can be ext4, f2fs or raw.
     * @return result SUCCESS if the operation is successful,
     * FAILURE_UNKNOWN if the partition is invalid or does not require
     * reformatting.
     */
    getPartitionType(string partitionName) generates (FileSystemType type, Result result);

    /**
     * Executes a fastboot OEM command.
     *
     * @param oemCmd The oem command that is passed to the fastboot HAL.
     * @response result Returns the status SUCCESS if the operation is
     * successful,
     * INVALID_ARGUMENT for bad arguments,
     * FAILURE_UNKNOWN for an invalid/unsupported command.
     */
    doOemCommand(string oemCmd) generates (Result result);

};

启用 fastbootd

要在设备上启用 fastbootd

  1. device.mk 中的 PRODUCT_PACKAGES 中添加 fastbootdPRODUCT_PACKAGES += fastbootd

  2. 确保将 fastboot HAL、启动控制 HAL 和健康 HAL 打包为 recovery 镜像的一部分。

  3. 添加 fastbootd 所需的任何设备特定的 SELinux 权限。例如,fastbootd 需要对设备特定分区的写入权限才能刷写该分区。此外,fastboot HAL 实现也可能需要设备特定的权限。

要验证用户空间 fastboot,请运行供应商测试套件 (VTS)

刷写供应商 ramdisk

Android 12 及更高版本提供了通过添加 fastboot 命令来刷写 ramdisk 的支持,该命令从设备中提取完整的 vendor_boot 镜像。该命令提示主机端 fastboot 工具读取供应商启动标头、重新镜像并刷写新镜像。

为了提取完整的 vendor_boot 镜像,在 Android 12 的 fastboot 协议和 fastbootd 实现的协议中都添加了命令 fetch:vendor_boot。请注意,fastbootd确实实现了此命令,但启动加载程序本身可能没有。OEM 可以将 fetch:vendor_boot 命令添加到其启动加载程序协议的实现中。但是,如果在启动加载程序模式下无法识别该命令,则在启动加载程序模式下刷写单个供应商 ramdisk 不是供应商支持的选项。

启动加载程序更改

命令 getvar:max-fetch-sizefetch:namefastbootd 中实现。要支持在启动加载程序中刷写供应商 ramdisk,您必须实现这两个命令。

Fastbootd 更改

getvar:max-fetch-size 类似于 max-download-size。它指定设备在一个 DATA 响应中可以发送的最大大小。驱动程序提取的大小不得大于此值。

fetch:name[:offset[:size]] 对设备执行一系列检查。如果以下所有条件都为真,则 fetch:name[:offset[:size]] 命令返回数据

  • 设备正在运行可调试版本。
  • 设备已解锁(启动状态为橙色)。
  • 提取的分区名称为 vendor_boot
  • size 值落在 0 < size <= max-fetch-size 范围内。

当这些条件得到验证后,fetch:name[:offset[:size]] 返回分区大小和偏移量。请注意以下事项

  • fetch:name 等效于 fetch:name:0,也等效于 fetch:name:0:partition_size
  • fetch:name:offset 等效于 fetch:name:offset:(partition_size - offset)

因此,fetch:name[:offset[:size]] = fetch:name:offset:(partition_size - offset)

offsetpartition_size(或两者)未指定时,将使用默认值,其中 offset 的默认值为 0,size 的默认值为 partition_size - offset 的计算值。

  • 指定了偏移量,未指定大小:size = partition_size - offset
  • 两者都未指定:对两者都使用默认值,size = partition_size - 0。

例如,fetch:foo 提取偏移量为 0 的整个 foo 分区。

驱动程序更改

为了实现驱动程序更改,fastboot 工具中添加了命令。每个命令都链接到 Fastboot 命令表中的完整定义。

  • fastboot fetch vendor_boot out.img

    • 调用 getvar max-fetch-size 以确定 chunk 大小。
    • 调用 getvar partition-size:vendor_boot[_a] 以确定整个分区的大小。
    • 为每个 chunk 调用 fastboot fetch vendor_boot[_a]:offset:size。(chunk 大小大于 vendor_boot 大小,因此通常只有一个 chunk。)
    • 将数据拼接在一起,形成 out.img
  • fastboot flash vendor_boot:default vendor-ramdisk.img

    这是 flash 命令的特殊变体。它提取 vendor_boot 镜像,如同调用了 fastboot fetch 一样。

    • 如果供应商启动标头是 版本 3,它会执行以下操作
      • 将供应商 ramdisk 替换为给定的镜像。
      • 刷写新的 vendor_boot 镜像。
    • 如果供应商启动标头是 版本 4,它会执行以下操作
      • 将整个供应商 ramdisk 替换为给定的镜像,以便给定的镜像成为 vendor_boot 镜像中唯一的供应商 ramdisk 片段。
      • 重新计算供应商 ramdisk 表中的大小和偏移量。
      • 刷写新的 vendor_boot 镜像。
  • fastboot flash vendor_boot:foo vendor-ramdisk.img

    提取 vendor_boot image,如同调用了 fastboot fetch 一样。

    • 如果供应商启动标头是版本 3,则返回错误。
    • 如果供应商启动标头是版本 4,它会执行以下操作

      • 查找名称为 ramdisk_<var>&lt;foo></var> 的供应商 ramdisk 片段。如果未找到,或者存在多个匹配项,则返回错误。
      • 将供应商 ramdisk 片段替换为给定的镜像。
      • 重新计算供应商 ramdisk 表中每个大小和偏移量。
      • 刷写新的 vendor_boot 镜像。
    • 如果未指定 <foo>,它会尝试查找 ramdisk_

mkbootimg

名称 default 保留在 Android 12 及更高版本中用于命名供应商 ramdisk 片段。虽然 fastboot flash vendor_boot:default 语义保持不变,但您绝不能将 ramdisk 片段命名为 default

SELinux 更改

fastbootd.te 中进行了更改,以支持刷写供应商 ramdisk。