Move fastboot to userspace

Fastboot is the name of a bootloader module and mode. Android 10 and higher supports resizable partitions by relocating the fastboot implementation from bootloader to userspace. This relocation enables moving the flashing code into a maintainable and testable common location with only the vendor-specific parts of fastboot implemented by a hardware abstraction layer (HAL). In addition, Android 12 and higher supports flashing ramdisks through an added fastboot command.

Unify fastboot and recovery

Because userspace fastboot and recovery are similar, you can merge them into one partition or binary. This provides advantages such as using less space, having fewer partitions overall, and having fastboot and recovery share their kernel and libraries.

Fastbootd is the name of a userspace daemon and mode. To support fastbootd, the bootloader must implement a new boot control block (BCB) command of boot-fastboot. To enter fastbootd mode, bootloader writes boot-fastboot into the command field of the BCB message and leaves the recovery field of BCB unchanged (to enable restarting any interrupted recovery tasks). The status, stage, and reserved fields remain unchanged as well. The bootloader loads and boots into the recovery image upon seeing boot-fastboot in the BCB command field. Recovery then parses the BCB message and switches to fastbootd mode.

ADB commands

This section describes the adb command for integrating fastbootd. The command has different results, depending on whether it's executed by system or by recovery.

Command Description
reboot fastboot
  • Reboots into fastbootd (system).
  • Enters fastbootd directly without a reboot (recovery).

Fastboot commands

This section describes the fastboot commands for integrating fastbootd, including new commands for flashing and managing logical partitions. Some commands have different results, depending on whether they've been executed by bootloader or by fastbootd.

Command Description
reboot recovery
  • Reboots into recovery (bootloader).
  • Enters recovery directly without a reboot (fastbootd).
reboot fastboot Reboots into fastbootd.
getvar is-userspace
  • Returns yes (fastbootd).
  • Returns no (bootloader).
getvar is-logical:<partition> Returns yes if the given partition is a logical partition, no otherwise. Logical partitions support all of the commands listed below.
getvar super-partition-name Returns the name of the super partition. The name includes the current slot suffix if the super partition is an A/B partition (it usually isn't).
create-logical-partition <partition> <size> Creates a logical partition with the given name and size. The name must not already exist as a logical partition.
delete-logical-partition <partition> Deletes the given logical partition (effectively wipes the partition).
resize-logical-partition <partition> <size> Resizes the logical partition to the new size without changing its contents. Fails if there isn't enough space available to perform the resize.
update-super <partition> Merges changes to the super partition metadata. If a merge isn't possible (for example, the format on the device is an unsupported version), then this command fails. An optional wipe parameter overwrites the device's metadata, rather than performing a merge.
flash <partition><filename> ] Writes a file to a flash partition. Device must be in the unlocked state.
erase <partition> Erases a partition (not required to be secure erase). Device must be in the unlocked state.
getvar <variable> | all Displays a bootloader variable, or all variables. If the variable doesn't exist, returns an error.
set_active <slot>

Sets the given A/B booting slot as active. On the next boot attempt, the system boots from the specified slot.

For A/B support, slots are duplicated sets of partitions that can be booted from independently. Slots are named a, b, and so on, and differentiated by adding the suffixes _a, _b, and so on to the partition name.

reboot Reboots device normally.
reboot-bootloader (or reboot bootloader) Reboots device into bootloader.
fastboot fetch vendor_boot <out.img>

Use in Android 12 and higher to support flashing vendor ramdisks.

Gets the entire partition size and the chunk size. Gets data for each chunk, then stitches the data together to <out.img>

For details, see fastboot fetch vendor_boot <out.img>.

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

Use in Android 12 and higher to support flashing vendor ramdisks.

This is a special variant of the flash command. It performs a fetch vendor_boot image function, as if fastboot fetch was called. The new vendor_boot image it flashes depends on whether the boot header version is version 3 or version 4.

For details, see fastboot flash vendor_boot:default <vendor-ramdisk.img>.

fastboot flash vendor_boot:<foo> <vendor-ramdisk.img> Use in Android 12 and higher to support flashing vendor ramdisks.

Fetches the vendor_boot image. Returns an error if the vendor boot header is version 3. If it's version 4, it finds the correct vendor ramdisk fragment (if available). It replaces that with the given image, recalculates sizes and offsets, and flashes the new vendor_boot image.

For details, see fastboot flash vendor_boot:<foo> <vendor-ramdisk.img>

Fastboot and bootloader

The bootloader flashes the bootloader, radio, and boot/recovery partitions, after which the device boots into fastboot (userspace) and flashes all other partitions. The bootloader should support the following commands.

Command Description
download Downloads the image to flash.
flash recovery <image>/ flash boot <image>/ flash bootloader <image>/ Flashes recovery/boot partition and bootloader.
reboot Reboots the device.
reboot fastboot Reboots to fastboot.
reboot recovery Reboots to recovery.
getvar Gets a bootloader variable that is required for flashing of recovery/boot image (for example, current-slot and max-download-size).
oem <command> Command defined by OEM.

Dynamic partitions

The bootloader must not allow the flashing or erasing of dynamic partitions and must return an error if these operations are attempted. For retrofitted dynamic partition devices, the fastboot tool (and bootloader) supports a force mode to directly flash a dynamic partition while in bootloader mode. For example, if system is a dynamic partition on the retrofitted device, using the fastboot --force flash system command enables the bootloader (instead of fastbootd) to flash the partition.

Off-mode charging

If a device supports off-mode charging or otherwise autoboots into a special mode when power is applied, an implementation of the fastboot oem off-mode-charge 0 command must bypass these special modes, so that the device boots as if the user had pressed the power button.

Fastboot OEM HAL

To completely replace bootloader fastboot, fastboot must handle all existing fastboot commands. Many of these commands are from OEMs and are documented but require a custom implementation. Many OEM-specific commands aren't documented. To handle such commands, the fastboot HAL specifies the required OEM commands. OEMs can also implement their own commands.

The definition of fastboot HAL is as follows:

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);

};

Enable fastbootd

To enable fastbootd on a device:

  1. Add fastbootd to PRODUCT_PACKAGES in device.mk: PRODUCT_PACKAGES += fastbootd.

  2. Ensure that the fastboot HAL, boot control HAL, and health HAL are packaged as part of the recovery image.

  3. Add any device-specific SEPolicy permissions required by fastbootd. For example, fastbootd requires write access to a device-specific partition to flash that partition. In addition, fastboot HAL implementation may also require device-specific permissions.

To validate userspace fastboot, run the Vendor Test Suite (VTS).

Flash vendor ramdisks

Android 12 and higher provides support for flashing ramdisks with an added fastboot command that pulls the full vendor_boot image from a device. The command prompts the host-side fastboot tool to read the vendor boot header, reimage, and flash the new image.

To pull the full vendor_boot image, the command fetch:vendor_boot was added to both the fastboot protocol, and the fastbootd implementation of the protocol in Android 12. Note that fastbootd does implement this, but the bootloader itself might not. OEMs can add the fetch:vendor_boot command to their bootloader implementation of the protocol. However, if the command isn't recognized in bootloader mode, then flashing individual vendor ramdisks in bootloader mode isn't a vendor-supported option.

Bootloader changes

The commands getvar:max-fetch-size and fetch:name are implemented in fastbootd. To support flashing vendor ramdisks in bootloader, you must implement these two commands.

Fastbootd changes

getvar:max-fetch-size is similar to max-download-size. It specifies the maximum size that the device can send in one DATA response. The driver must not fetch a size larger than this value.

fetch:name[:offset[:size]] performs a series of checks on the device. If all of the following are true, the fetch:name[:offset[:size]] command returns data:

  • The device is running a debuggable build.
  • The device is unlocked (boot state orange).
  • The fetched partition name is vendor_boot.
  • The size value falls within 0 < size <= max-fetch-size.

When these are verified, fetch:name[:offset[:size]] returns the partition size and offset. Note the following:

  • fetch:name is equivalent to fetch:name:0, which is equivalent to fetch:name:0:partition_size.
  • fetch:name:offset is equivalent to fetch:name:offset:(partition_size - offset)

Therefore fetch:name[:offset[:size]] = fetch:name:offset:(partition_size - offset).

When offset or partition_size (or both) are unspecified, the default values are used, which for offset is 0, and for size is the calculated value of partition_size - offset.

  • Offset specified, size unspecified: size = partition_size - offset
  • Neither specified: default values used for both, size = partition_size - 0.

For example, fetch:foo fetches the entire foo partition at offset 0.

Driver changes

Commands were added to the fastboot tool to implement driver changes. Each is linked to its full definition in the table of Fastboot commands.

  • fastboot fetch vendor_boot out.img

    • Calls getvar max-fetch-size to determine the chunk size.
    • Calls getvar partition-size:vendor_boot[_a] to determine the size of the entire partition.
    • Calls fastboot fetch vendor_boot[_a]:offset:size for each chunk. (The chunk size is greater than the vendor_boot size, so there's normally only one chunk.)
    • Stitches the data together, to out.img.
  • fastboot flash vendor_boot:default vendor-ramdisk.img

    This is a special variant of the flash command. It fetches the vendor_boot image, as if fastboot fetch was called.

    • If the vendor boot is header version 3, it does the following:
      • Replaces the vendor ramdisk with the given image.
      • Flashes the new vendor_boot image.
    • If the vendor boot header is version 4, it does the following:
      • Replaces the whole vendor ramdisk with the given image so that the given image becomes the only vendor ramdisk fragment in the vendor_boot image.
      • Recalculates the size and offset in the vendor ramdisk table.
      • Flashes the new vendor_boot image.
  • fastboot flash vendor_boot:foo vendor-ramdisk.img

    Fetches vendor_boot image, as if fastboot fetch was called.

    • If the vendor boot header is version 3, it returns an error.
    • If the vendor boot header is version 4, it does the following:

      • Finds the vendor ramdisk fragment with name ramdisk_<var>&lt;foo></var>. If not found, or if there are multiple matches, returns an error.
      • Replaces the vendor ramdisk fragment with the given image.
      • Recalculates each size and offset in the vendor ramdisk table.
      • Flashes the new vendor_boot image.
    • If <foo> isn't specified, it tries to find ramdisk_.

mkbootimg

The name default is reserved for naming vendor ramdisk fragments in Android 12 and higher. While the fastboot flash vendor_boot:default semantics remain the same, you must not name your ramdisk fragments as default.

SELinux changes

A change was made in fastbootd.te to support flashing vendor ramdisks.