zoukankan      html  css  js  c++  java
  • 高通安卓: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 
    ", Status));
      DEBUG ((EFI_D_ERROR, "reading key status: %d 
    ", 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
    "));
            SetFFBMCommand(); // 设置 androidboot.mode=ffbm-xxx
        }
      } else if (Status == EFI_DEVICE_ERROR) {
        DEBUG ((EFI_D_ERROR, "Error reading key status: %r
    ", 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
    
    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    Go视频教程
    Mysql常用
    同步Redux
    React跨组件通讯
    React组件通讯
    React
    git生成公钥和私钥
    常用经典算法---希尔排序
    string和c_str()使用时的坑
    腾讯云python网站开发环境搭建
  • 原文地址:https://www.cnblogs.com/schips/p/how_qualcomm_android_run_ffbm_procedure.html
Copyright © 2011-2022 走看看