將 Fastboot 移至使用者空間

「Fastboot」是系統啟動載入程式模組和模式的名稱。Android 10 以上版本支援可調整大小的分割區,方法是將 Fastboot 實作項目從開機載入程式移至使用者空間。這項重定位作業可將刷機程式碼移至可維護及可測試的通用位置,並只透過硬體抽象層 (HAL) 實作 Fastboot 的供應商專屬部分。此外,Android 12 以上版本支援透過新增的 fastboot 指令刷寫 ramdisk。

統一 Fastboot 和 Recovery

由於使用者空間的 Fastboot 和 Recovery 類似,因此您可以將兩者合併為一個磁碟分割區或二進位檔。這項做法的優點包括減少空間用量、整體分割區數量較少,以及讓 Fastboot 和復原模式共用核心和程式庫。

Fastbootd 是使用者空間精靈和模式的名稱。 如要支援 fastbootd,系統啟動載入程式必須實作 boot-fastboot 的新開機控制區塊 (BCB) 指令。如要進入 fastbootd 模式,開機載入程式會將 boot-fastboot 寫入 BCB 訊息的指令欄位,並保持 BCB 的 recovery 欄位不變 (以便重新啟動任何中斷的復原工作)。status、stage 和 reserved 欄位也會維持不變。 看到 BCB 指令欄位中的 boot-fastboot 時,開機載入器會載入並啟動復原映像檔。然後,復原作業會剖析 BCB 訊息,並切換至 fastbootd 模式。

ADB 指令

本節說明如何使用 adb 指令整合 fastbootd。視執行指令的是系統還是復原功能,指令會產生不同的結果。

指令 說明
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 分割區 (通常不是),名稱會包含目前的 slot 字尾。
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 分區,必須複製分區集,並從中獨立啟動。版位名稱為 a、b 等,並在分區名稱中加入 _a、_b 等後置字串,藉此區分版位。

reboot 正常重新啟動裝置。
reboot-bootloader (或 reboot bootloader) 重新啟動裝置並進入系統啟動載入程式。
fastboot fetch vendor_boot <out.img>

在 Android 12 以上版本中使用,支援刷入供應商 ramdisk。

取得整個分區大小和區塊大小。取得每個區塊的資料,然後將資料縫合在一起,<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 版,系統會尋找正確的供應商 RAM 磁碟片段 (如有)。並以指定圖片取代該圖片、重新計算大小和位移,以及閃爍顯示新的 vendor_boot image。

詳情請參閱 fastboot flash vendor_boot:<foo> <vendor-ramdisk.img>

Fastboot 和系統啟動載入程式

系統啟動載入程式會刷寫 bootloader、radio 和 boot/recovery 分區,之後裝置會啟動進入 Fastboot (使用者空間),並刷寫所有其他分區。系統啟動載入程式應支援下列指令。

指令 說明
download 將映像檔下載到快閃記憶體。
flash recovery <image>/ flash boot <image>/ flash bootloader <image>/ 刷寫 recovery/boot 分區和啟動載入器。
reboot 重新啟動裝置。
reboot fastboot 重新啟動為 Fastboot。
reboot recovery 重新啟動進入復原模式。
getvar 取得刷寫復原/開機映像檔所需的系統啟動載入程式變數 (例如 current-slot 和 max-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 指令。原始設備製造商也可以實作自己的指令。

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 中將 fastbootd 新增至 PRODUCT_PACKAGES:PRODUCT_PACKAGES += fastbootd。

  2. 確認 fastboot HAL、啟動控制 HAL 和健康狀態 HAL 已封裝為復原映像檔的一部分。

  3. 新增 fastbootd 要求的任何裝置專屬 SEPolicy 權限。舉例來說,fastbootd 需要裝置專屬分區的寫入權限,才能將該分區刷入裝置。此外,實作 Fastboot HAL 也可能需要裝置專屬權限。

如要驗證使用者空間快速啟動,請執行供應商測試套件 (VTS)。

刷入供應商 ramdisk

Android 12 以上版本支援使用新增的 Fastboot 指令刷寫 RAM 磁碟,從裝置中提取完整的 vendor_boot 映像檔。這項指令會提示主機端的 Fastboot 工具讀取供應商啟動標頭、重新建立映像檔,並刷入新映像檔。

如要提取完整的 vendor_boot 映像檔,Android 12 的 fastboot 協定和協定的 fastbootd 實作項目都新增了 fetch:vendor_boot 指令。請注意,fastbootd 會實作這項功能,但開機載入程式本身可能不會。原始設備製造商 (OEM) 可以在通訊協定的開機載入程式實作中新增 fetch:vendor_boot 指令。不過,如果系統啟動載入程式模式無法辨識指令,則在系統啟動載入程式模式中刷入個別供應商 RAM 磁碟,並非供應商支援的選項。

系統啟動載入程式變更

指令 getvar:max-fetch-size 和 fetch:name 會在 fastbootd 中實作。如要在開機載入器中支援刷入供應商 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)。

如果未指定 offset 或 partition_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 來判斷區塊大小。
    • 呼叫 getvar partition-size:vendor_boot[_a] 來判斷整個分區的大小。
    • 針對每個區塊呼叫 fastboot fetch vendor_boot[_a]:offset:size。(區塊大小大於 vendor_boot 大小,因此通常只有一個區塊)。
    • 將資料縫合在一起,以 out.img 為單位。
  • fastboot flash vendor_boot:default vendor-ramdisk.img

    這是 flash 指令的特殊變體。並擷取 vendor_boot 圖片,就像呼叫 fastboot fetch 一樣。

    • 如果供應商啟動標頭是版本 3,則會執行下列動作:
      • 使用指定映像檔取代供應商 RAM 磁碟。
      • 刷寫新的 vendor_boot 映像檔。
    • 如果供應商啟動標頭是版本 4,則會執行下列動作:
      • 以指定圖片取代整個供應商 ramdisk,使指定圖片成為 vendor_boot 映像檔中唯一的供應商 ramdisk 片段。
      • 重新計算供應商 RAM 磁碟表中的大小和偏移。
      • 刷寫新的 vendor_boot 映像檔。
  • fastboot flash vendor_boot:foo vendor-ramdisk.img

    擷取 vendor_boot image,如同呼叫 fastboot fetch。

    • 如果供應商啟動標頭為第 3 版,則會傳回錯誤。
    • 如果供應商啟動標頭為第 4 版,則會執行下列操作:

      • 找出名稱為 ramdisk_<var>&lt;foo></var> 的供應商 RAM 磁碟片段。如果找不到相符項目,或是有多個相符項目,則會傳回錯誤。
      • 使用指定映像檔取代供應商 ramdisk 片段。
      • 重新計算供應商 RAM 磁碟表中的每個大小和偏移。
      • 刷寫新的 vendor_boot 映像檔。
    • 如未指定 <foo>,則會嘗試尋找 ramdisk_。

mkbootimg

在 Android 12 以上版本中,default 這個名稱保留給供應商 ramdisk 片段命名。雖然 fastboot flash vendor_boot:default 語意保持不變,但不得將 ramdisk 片段命名為 default。

SELinux 變更

為支援刷入供應商 ramdisk, fastbootd.te 進行了變更。