高通安卓:androidboot.mode參數控制系統流程原理


高通安卓:androidboot.mode參數控制系統流程原理

參考:https://blog.csdn.net/guofeizhi/article/details/106644773

背景

在做出廠功能測試的時候,看到之前做開發時進入ffbm模式以后的cmdline中對應的字段為androidboot.mode=ffbm-01;而現在項目中的cmdline對應的是androidboot.mode=ffbm-02。而且界面上也不太一樣,一種是C/C++實現的;一種是直接可以在界面上點擊的QMMI。

現在我也找到了答案:

FFBM對應ffbm-00或ffbm-00

// system/core/fs_mgr/fs_mgr.cpp
int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
{
    int i = 0;
    int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
    int error_count = 0;
    int mret = -1;
    int mount_errno = 0;
    int attempted_idx = -1;
    FsManagerAvbUniquePtr avb_handle(nullptr);
    char propbuf[PROPERTY_VALUE_MAX];
    bool is_ffbm = false;

    if (!fstab) {
        return FS_MGR_MNTALL_FAIL;
    }

    /**get boot mode*/
    property_get("ro.bootmode", propbuf, "");
    if ((strncmp(propbuf, "ffbm-00", 7) == 0) || (strncmp(propbuf, "ffbm-01", 7) == 0))
        is_ffbm = true;

    // ..
}

QMMI對應ffbm-02

// vendor/qcom/proprietary/commonsys/fastmmi/qmmi/src/com/qualcomm/qti/qmmi/framework/QmmiReceiver.java
public class QmmiReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String bootmode = Utils.getSystemProperties("ro.bootmode", "00");
        LogUtils.logi("bootmode:" + bootmode);
        if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED) && bootmode.equals("ffbm-02")) {
            LogUtils.logi("receive boot complete");
            startMainActivity(context);
        } else if (intent.getAction().equals("android.provider.Telephony.SECRET_CODE")) {
            LogUtils.logi("receive SECRET_CODE ");
            startMainActivity(context);
        }
    }

    // ...
}

流程解析

BootLoader針對ffbm的處理

以uefi為例。

判斷進入FFBM模式

路徑:bootable/bootloader/edk2/QcomModulePkg/Application/LinuxLoader/LinuxLoader.c

EFI_STATUS EFIAPI  __attribute__ ( (no_sanitize ("safe-stack")))
LinuxLoaderEntry (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
  // ...
  Status = GetKeyPress (&KeyPressed);
  DEBUG ((EFI_D_ERROR, "reading key status: %r \n", Status));
  DEBUG ((EFI_D_ERROR, "reading key status: %d \n", KeyPressed));
  if (Status == EFI_SUCCESS) {
    if (KeyPressed == SCAN_DOWN ||KeyPressed == SCAN_DELETE)
      BootIntoFastboot = TRUE;
    if (KeyPressed == SCAN_UP)
      BootIntoRecovery = TRUE;
    if (KeyPressed == SCAN_ESC)
      RebootDevice (EMERGENCY_DLOAD);
    if (KeyPressed == SCAN_HOME)  //POWER+VOL UP will generate SCAN_HOME, detect this key, it will enter ffbm mode
    {
        DEBUG ((EFI_D_ERROR, "go to ffbm mode\n"));
        SetFFBMCommand(); // 設置 androidboot.mode=ffbm-xxx
    }
  } else if (Status == EFI_DEVICE_ERROR) {
    DEBUG ((EFI_D_ERROR, "Error reading key status: %r\n", Status));
    goto stack_guard_update_default;
  }
  // ...
}

在cmdline中傳遞啟動模式

BootLoader把對應的啟動模式通過 command line的方式傳送給內核。

在uefi中解釋這個過程需要一定的UEFI基礎,因此不詳細展開,但是實際上基於的就是檢查SetFFBMCommand的調用。

其中涉及到了針對分區的寫操作。

// bootable/bootloader/edk2/QcomModulePkg/Library/BootLib/Recovery.c
EFI_STATUS SetFFBMCommand(VOID)
{
  EFI_STATUS Status = EFI_SUCCESS;
  CHAR8 FfbmPageBuffer[FFBM_MODE_BUF_SIZE] = "";
  EFI_GUID Ptype = gEfiMiscPartitionGuid; // 雜項設備分區
  MemCardType CardType = UNKNOWN;
  CardType = CheckRootDeviceType ();
  if (CardType == NAND) {
    Status = GetNandMiscPartiGuid (&Ptype);
    if (Status != EFI_SUCCESS) {
      return Status;
    }
  }
  AsciiSPrint (FfbmPageBuffer, sizeof (FfbmPageBuffer), "ffbm-02");
  WriteToPartition (&Ptype, FfbmPageBuffer, sizeof (FfbmPageBuffer));
  return Status;
}

內核

路徑:system/core/init/init.cpp

內核解析cmdline,並設置對應的系統屬性。

int main(int argc, char** argv) {
    
    // ...
    
    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    process_kernel_dt();
    process_kernel_cmdline(); // 解析命令行

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    export_kernel_boot_props();
    
    // ...
    
    // Don't mount filesystems or start core system services in charger mode.
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        am.QueueEventTrigger("charger");
    } else {
        am.QueueEventTrigger("late-init");
    }
    // ...
}

解析command line

路徑:

  • system/core/init/util.cpp
  • system/core/init/init.cpp
// system/core/init/util.cpp
void import_kernel_cmdline(bool in_qemu,
                           const std::function<void(const std::string&, const std::string&, bool)>& fn) {
    std::string cmdline;
    android::base::ReadFileToString("/proc/cmdline", &cmdline);

    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
        std::vector<std::string> pieces = android::base::Split(entry, "=");
        if (pieces.size() == 2) {
            fn(pieces[0], pieces[1], in_qemu);
        }
    }
}

// system/core/init/init.cpp
static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
    if (key.empty()) return;

    if (for_emulator) {
        // In the emulator, export any kernel option with the "ro.kernel." prefix.
        property_set("ro.kernel." + key, value);
        return;
    }
    
    // 實際上的cmdline : androidboot.mode=ffbm-02

    if (key == "qemu") {
        strlcpy(qemu, value.c_str(), sizeof(qemu));
    } else if (android::base::StartsWith(key, "androidboot.")) {
        // 寫配置,相當於:ro.boot.mode = ffbm-02
        property_set("ro.boot." + key.substr(12), value);
    }
}

// system/core/init/init.cpp
static void process_kernel_cmdline() {
    // The first pass does the common stuff, and finds if we are in qemu.
    // The second pass is only necessary for qemu to export all kernel params
    // as properties.
    import_kernel_cmdline(false, import_kernel_nv);
    if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}

設置對應的系統屬性

將解析cmdline寫的配置導出(實際上就是換個名字)。

static void export_kernel_boot_props() {
    struct {
        const char *src_prop;
        const char *dst_prop;
        const char *default_value;
    } prop_map[] = {
        { "ro.boot.serialno",   "ro.serialno",   "", },
        { "ro.boot.mode",       "ro.bootmode",   "unknown", },
        { "ro.boot.baseband",   "ro.baseband",   "unknown", },
        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
        { "ro.boot.hardware",   "ro.hardware",   "unknown", },
        { "ro.boot.revision",   "ro.revision",   "0", },
    };
    for (size_t i = 0; i < arraysize(prop_map); i++) {
        std::string value = GetProperty(prop_map[i].src_prop, "");
        property_set(prop_map[i].dst_prop, (!value.empty()) ? value : prop_map[i].default_value);
    }
}

最終:

[ro.boot.mode]: [ffbm-00]
[ro.bootmode]: [ffbm-00]

此后,關於啟動模式的處理都以ro.bootmode為准。

安卓系統啟動相關進程

ro.bootmode設置了以后,對應的rc解析就會根據這些內容進行處理。

init.rc

還記得嗎,在init.cpp中執行了am.QueueEventTrigger("late-init");導致下面的rc被執行。

路徑:system/core/rootdir/init.rc

on late-init
    trigger early-fs

    # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
    # '--early' can be specified to skip entries with 'latemount'.
    # /system and /vendor must be mounted by the end of the fs stage,
    # while /data is optional.
    trigger factory-fs
    trigger fs
    trigger post-fs

    # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
    # to only mount entries with 'latemount'. This is needed if '--early' is
    # specified in the previous mount_all command on the fs stage.
    # With /system mounted and properties form /system + /factory available,
    # some services can be started.
    trigger late-fs

    # Now we can mount /data. File encryption requires keymaster to decrypt
    # /data, which in turn can only be loaded when system properties are present.
    trigger post-fs-data

    # Now we can start zygote for devices with file based encryption
    trigger zygote-start

    # Load persist properties and override properties (if enabled) from /data.
    trigger load_persist_props_action

    # Remove a file to wake up anything waiting for firmware.
    trigger firmware_mounts_complete

    trigger early-boot
    trigger boot
    trigger mmi # 觸發 了 mmi 這個觸發器

init.qcom.factory.rc

路徑:device/qcom/common/rootdir/etc/init.qcom.factory.rc

on property:vendor.sys.boot_mode=ffbm
    write ${persist.vendor.mmi.misc_dev_path} "ffbm-01"

on property:vendor.sys.boot_mode=qmmi
    write ${persist.vendor.mmi.misc_dev_path} "ffbm-02"

on property:vendor.sys.boot_mode=normal
    write ${persist.vendor.mmi.misc_dev_path} "normal"

# Creating a scratch storage on /data for factory testing.
on factory-fs && property:ro.bootmode=ffbm-00
    mount tmpfs tmpfs /data

on factory-fs && property:ro.bootmode=ffbm-01
    mount tmpfs tmpfs /data
    
# aligned the usb port with system standard, otherwise if only diag be added
# Then in QMMI mode, the whole Andoid be booted, but due to the ro.bootmode is
# not normal/unknow, then when it apply the default funcs, it will turn to MTP
# which cause the diag/Wwan/modem port all be lost in qmmi mode. Details:
# UsbDeviceManager.java---->getDefaultFunctions and trySetEnabledFunctions
on property:persist.vendor.usb.config=*
    setprop persist.sys.usb.ffbm-02.func ${persist.vendor.usb.config}

on mmi && property:ro.bootmode=ffbm-00
    # ========================================================
    #              This is FFBM only settings.
    # ========================================================
    #mkdir for factory data files.
    mkdir /mnt/vendor/persist/FTM_AP 0750 system system

    start fastmmi
    # start qcom-post-boot to set the misc partition path property value
    start qcom-post-boot
    start mmi_diag

on mmi && property:ro.bootmode=ffbm-01
    # ========================================================
    #              This is FFBM only settings.
    # ========================================================
    #mkdir for factory data files.
    mkdir /mnt/vendor/persist/FTM_AP 0750 system system

    start fastmmi
    ## start qcom-post-boot to set the misc partition path property value
    start qcom-post-boot
    start mmi_diag

on mmi && property:ro.bootmode=ffbm-02 #▲ 注意,啟動了這個
    #mkdir for factory data files.
    mkdir /mnt/vendor/persist/FTM_AP 0750 system system

    start fastmmi
    ## start qcom-post-boot to set the misc partition path property value
    start qcom-post-boot
    start mmi_diag

on property:persist.vendor.usb.config=* && property:ro.bootmode=ffbm-00
    setprop sys.usb.config ${persist.vendor.usb.config}

on property:persist.vendor.usb.config=* && property:ro.bootmode=ffbm-01
    setprop sys.usb.config ${persist.vendor.usb.config}

on property:persist.vendor.usb.config=* && property:ro.bootmode=ffbm-02
    setprop sys.usb.config ${persist.vendor.usb.config}

#### 做最小系統的啟動
on ffbm
    trigger early-fs
    trigger factory-fs
    trigger fs
    trigger post-fs

    # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
    # to only mount entries with 'latemount'. This is needed if '--early' is
    # specified in the previous mount_all command on the fs stage.
    # With /system mounted and properties form /system + /factory available,
    # some services can be started.
    trigger late-fs

    # Now we can mount /data. File encryption requires keymaster to decrypt
    # /data, which in turn can only be loaded when system properties are present.
    trigger post-fs-data

    # Now we can start zygote for devices with file based encryption
    trigger zygote-start

    # Load persist properties and override properties (if enabled) from /data.
    trigger load_persist_props_action

    # Remove a file to wake up anything waiting for firmware.
    trigger firmware_mounts_complete

    trigger early-boot
    trigger boot
    trigger mmi


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM