MTK平台修改Bootloader源代码,让Android设备一通电就自动开机

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/CharlesSimonyi/article/details/82236494

为什么要一通电就自动开机

总所周知,一台Android设备,默认情况下,插上USB供电,都是显示正在充电的电池图标,按住开机键才能开机。如果你手里只有一台手机,按住开机键去开机很容易,如果你手里有1000台手机呢?通常见于微信推广、微信站街、手游工作室、刷单工作室等,往往拥有数百台数千台手机,使用群控和脚本批量控制这些手机做一些interesting的事情。这些工作室往往使用定制的手机,手里握着全套Android源代码,这样可以方便的做很多root都做不到的事情。那么一个工作室如果有数百台手机,如果断电了,需要开机,或者需要重启,都是很痛苦的事情,如果能让这些Android手机一通电就自动开机,岂不是方便极了。那么在有全套Android源代码的情况下,如何分析代码并修改去实现这一功能呢?

思路

其实一通电就开机这个功能,和Android没什么关系,分析的时候,也不要再去看Android源代码了。首先了解一下Android设备的启动流程:

启动流程

实际上通电以后,屏幕上出现电池充电的图标,就一直停留在Bootloader这个步骤了,电池充电和屏幕显示充电的动画都是在这里面实现的,这个时候连Linux内核都还没加载,更不用说Android了。
所以要实现这个功能,首先锁定代码位置,那就是Android的Bootloader,通常是LK,代码位于bootable/bootloader/lk/。锁定了范围,只需要分析这里面的代码就行了。LK的代码量并不多,只有几千行,借助开发板使用串口打LOG调试,观察LOG输出内容,并以此为关键词在代码中搜索,定位LOG对应的代码,相信你很快就能找到方法。这里有网上找到的两篇文章,以MTK平台为例,分析了按下开机键后,由Preloader -> LK -> Kernel 的过程,结合文章中的时序图去阅读LK代码,会得到事半功倍的效果,相信你很快就能分析出来,充电的时候,代码执行到那里,按住开机键的时候,代码执行到了哪里。

深入MTK平台bootloader启动之【 Pre-loader -> Lk】分析笔记
深入MTK平台bootloader启动之【 lk -> kernel】分析笔记

MTK平台改法

有了思路以后,使用开发板串口打LOG,通电进入到充电界面后,观察串口输出的日志,用日志内容中的关键词去LK代码中搜索,进一步定位关键代码位置,通过阅读代码,bootable\bootloader\lk\platform\mt6582\boot_mode.c 中的 void boot_mode_select(void) 函数引起了我的注意,看函数名,很可能是通电以后,选择如何boot,是开机呢,还是充电呢,还是进入recovery。

void boot_mode_select(void)
{

    int factory_forbidden = 0;
  //  int forbid_mode;
/*We put conditions here to filer some cases that can not do key detection*/
    extern int kedump_mini(void) __attribute__((weak));
    if (kedump_mini) 
    {
    if (kedump_mini()) 
    {
        mrdump_check();
        return;
    }
    }
    if (meta_detection())
    {
      return;
    }

    mrdump_check();

#if defined (HAVE_LK_TEXT_MENU)
/*Check RTC to know if system want to reboot to Fastboot*/
    if(Check_RTC_PDN1_bit13())
    {
      printf("[FASTBOOT] reboot to boot loader\n");
      g_boot_mode = FASTBOOT;
      Set_Clr_RTC_PDN1_bit13(false);
          return;
    }
    /*If forbidden mode is factory, cacel the factory key detection*/ 
    if(g_boot_arg->sec_limit.magic_num == 0x4C4C4C4C)
    {
        if(g_boot_arg->sec_limit.forbid_mode == F_FACTORY_MODE)
        {
            //Forbid to enter factory mode
            printf("%s Forbidden\n",MODULE_NAME);
            factory_forbidden=1;
        }
    }
  //  forbid_mode = g_boot_arg->boot_mode &= 0x000000FF;
    /*If boot reason is power key + volumn down, then
     disable factory mode dectection*/
    if(mtk_detect_pmic_just_rst())
    {
          factory_forbidden=1;
    }
    /*Check RTC to know if system want to reboot to Recovery*/
    if(Check_RTC_Recovery_Mode())
    {
          g_boot_mode = RECOVERY_BOOT;
          return;
    }
    /*If MISC Write has not completed  in recovery mode 
     before system reboot, go to recovery mode to
    finish remain tasks*/     
    if(unshield_recovery_detection())
    {
        return;
    }
    ulong begin = get_timer(0);

/*we put key dectection here to detect key which is pressed*/
    printf("eng build\n");
    printf("MT65XX_FACTORY_KEY 0x%x\n",MT65XX_FACTORY_KEY);
    printf("MT65XX_BOOT_MENU_KEY 0x%x\n",MT65XX_BOOT_MENU_KEY);
    printf("MT65XX_RECOVERY_KEY 0x%x\n",MT65XX_RECOVERY_KEY);
    while(get_timer(begin)<50)
    {


        if(!factory_forbidden){
                   if(mtk_detect_key(MT65XX_FACTORY_KEY))
                   {
                          printf("%s Detect key\n",MODULE_NAME);
                          printf("%s Enable factory mode\n",MODULE_NAME);
                          g_boot_mode = FACTORY_BOOT;
                          //video_printf("%s : detect factory mode !\n",MODULE_NAME);
                          return;
                   }
        }

        if(mtk_detect_key(MT65XX_BOOT_MENU_KEY))
         {
                           printf("\n%s Check  boot menu\n",MODULE_NAME);
                           printf("%s Wait 50ms for special keys\n",MODULE_NAME);
                           mtk_wdt_disable();
                           boot_mode_menu_select();
                           mtk_wdt_init();
                           return;  
         }
#ifdef MT65XX_RECOVERY_KEY
            if(mtk_detect_key(MT65XX_RECOVERY_KEY))
           {
                printf("%s Detect cal key\n",MODULE_NAME);
                printf("%s Enable recovery mode\n",MODULE_NAME);
                g_boot_mode = RECOVERY_BOOT;
                //video_printf("%s : detect recovery mode !\n",MODULE_NAME);
                return;
            }
#endif                   
    }
#else

/*We put conditions here to filer some cases that can not do key detection*/

/*Check RTC to know if system want to reboot to Fastboot*/
#ifdef MTK_FASTBOOT_SUPPORT
  if(Check_RTC_PDN1_bit13())
  {
    dprintf(INFO,"[FASTBOOT] reboot to boot loader\n");
    g_boot_mode = FASTBOOT;
    Set_Clr_RTC_PDN1_bit13(false);
         return ;
  }
#endif

   /*If forbidden mode is factory, cacel the factory key detection*/
    if(g_boot_arg->sec_limit.magic_num == 0x4C4C4C4C)
    {
        if(g_boot_arg->sec_limit.forbid_mode == F_FACTORY_MODE)
        {
            //Forbid to enter factory mode
            printf("%s Forbidden\n",MODULE_NAME);
            factory_forbidden=1;
        }
    }
//    forbid_mode = g_boot_arg->boot_mode &= 0x000000FF;
    /*If boot reason is power key + volumn down, then 
     disable factory mode dectection*/
    if(mtk_detect_pmic_just_rst())
    {
          factory_forbidden=1;
    }
    /*Check RTC to know if system want to reboot to Recovery*/
    if(Check_RTC_Recovery_Mode())
    {
          g_boot_mode = RECOVERY_BOOT;
          return ;
    }
    /*If MISC Write has not completed  in recovery mode 
     and interrupted by system reboot, go to recovery mode to 
    finish remain tasks*/
    if(unshield_recovery_detection())
    {
        return ;
    }
    ulong begin = get_timer(0);
/*we put key dectection here to detect key which is pressed*/
while(get_timer(begin)<50){
#ifdef MTK_FASTBOOT_SUPPORT
    if(mtk_detect_key(MT_CAMERA_KEY))
    {
          dprintf(INFO,"[FASTBOOT]Key Detect\n");
          g_boot_mode = FASTBOOT;
          return ;
    }
#endif
    if(!factory_forbidden){
                if(mtk_detect_key(MT65XX_FACTORY_KEY))
                {
                       printf("%s Detect key\n",MODULE_NAME);
                       printf("%s Enable factory mode\n",MODULE_NAME);
                       g_boot_mode = FACTORY_BOOT;
                       //video_printf("%s : detect factory mode !\n",MODULE_NAME);
                       return ;
                }
     }
#ifdef MT65XX_RECOVERY_KEY
        if(mtk_detect_key(MT65XX_RECOVERY_KEY))
        {
             printf("%s Detect cal key\n",MODULE_NAME);
             printf("%s Enable recovery mode\n",MODULE_NAME);
             g_boot_mode = RECOVERY_BOOT;
             //video_printf("%s : detect recovery mode !\n",MODULE_NAME);
             return ;
         }
#endif
}

#endif

#ifdef MTK_KERNEL_POWER_OFF_CHARGING
    if(kernel_power_off_charging_detection())
    {
        printf(" < Kernel Power Off Charging Detection Ok> \n");
        return;
    }
    else
    {
        printf("< Kernel Enter Normal Boot > \n");
    }
#endif
}

boot_mode_select函数中的g_boot_mode变量尤为重要,不同的赋值决定着它boot什么。在boot_mode_select函数中可以看到所有的分支,对boot_mode_select的赋值,要么是NORMAL_BOOT要么是FASTBOOT要么是RECOVERY_BOOT,似乎都和充电界面没什么关系啊。但是注意函数的尾部:

#ifdef MTK_KERNEL_POWER_OFF_CHARGING
    if(kernel_power_off_charging_detection())
    {
        printf(" < Kernel Power Off Charging Detection Ok> \n");
        return;
    }
    else
    {
        printf("< Kernel Enter Normal Boot > \n");
    }
#endif

看到了power_off_charging,似乎和关机状态下的充电有关系,再看两个分支的printf,一个分支和充电相关,一个分支是Kernel Enter Normal Boot正常开机启动。马上搜索kernel_power_off_charging_detection的定义:
find -name “*.c” | xargs grep “kernel_power_off_charging_detection”
在bootable\bootloader\lk\platform\mt6582\mt_kernel_power_off_charging.c中找到了这个函数,看文件名,显然是和关机充电相关的代码:

BOOL kernel_power_off_charging_detection(void)
{
    int off_mode_status = 1;
    /* */
    if(is_force_boot()) {
        upmu_set_rg_chrind_on(0);
        printf("[%s] Turn off HW Led\n", __func__);
        return FALSE;
    }

    off_mode_status = get_off_mode_charge_status();
    printf("[%s] off_mode_status %d\n", __func__, off_mode_status);
    if(upmu_is_chr_det() == KAL_TRUE) {
        if (off_mode_status) {
            g_boot_mode = KERNEL_POWER_OFF_CHARGING_BOOT;
        } else {
            g_boot_mode = NORMAL_BOOT;
            upmu_set_rg_chrind_on(0);
            return FALSE;
        }
        return TRUE;
    }
    else {
        /* power off */
        #ifndef NO_POWER_OFF
        printf("[kernel_power_off_charging_detection] power off\n");
        mt6575_power_off();        
        #endif
        return FALSE;   
    }
    /* */
}

可以看到这里对g_boot_mode有两种赋值,一种是关机状态下充电,一种是正常开机。那么一个改动思路就是将:
g_boot_mode = KERNEL_POWER_OFF_CHARGING_BOOT;
改成
g_boot_mode = NORMAL_BOOT;
这样原本要进行关机充电的,改成了正常开机启动。

还有一种找关键代码的思路是,找到了g_boot_mode这个变量后,在LK代码目录中搜索,看看哪里对它进行了赋值:
find -name “*.c” | xargs grep “g_boot_mode = ”
可以看到,只有bootable\bootloader\lk\platform\mt6582\mt_kernel_power_off_charging.c 文件中对g_boot_mode赋值为KERNEL_POWER_OFF_CHARGING_BOOT,其它地方都是赋值为NORMAL_BOOT、FASTBOOT、RECOVERY_BOOT、ALARM_BOOT等,明显和充电无关。这样也能快速定位到关键代码,究竟是哪里,让它进入了关机充电那个界面。

此时还没有结束,我又注意到了这个宏MTK_KERNEL_POWER_OFF_CHARGING:

#ifdef MTK_KERNEL_POWER_OFF_CHARGING
    if(kernel_power_off_charging_detection())
    {
        printf(" < Kernel Power Off Charging Detection Ok> \n");
        return;
    }
    else
    {
        printf("< Kernel Enter Normal Boot > \n");
    }
#endif

在bootable\bootloader\lk\platform\mt6582\boot_mode.c的 boot_mode_select 函数中,如果代码运行到这里,又没定义MTK_KERNEL_POWER_OFF_CHARGING这个宏,宏内这段代码不会执行,那么此时g_boot_mode的赋值是什么?当然是初始值NORMAL_BOOT,当然这只是猜想,万一哪个函数里改变了它的值呢,接下来在这个位置插入打LOG的代码把g_boot_mode的值输出来看看,果然是NORMAL_BOOT。那么尝试取消定义MTK_KERNEL_POWER_OFF_CHARGING这个宏,是不是就能实现直接开机的效果呢?经过测试,果然如此。

这个宏的意思是是否允许在关机状态下充电,可以理解为,如果不允许,那么插电后,设备会开机再充电。
当然,宏的改法是MTK平台特有的,其它平台可以参考代码的改法,虽然不同平台的LK代码有差异,但总体流程还是不变的。

那么,如何取消这个宏的定义呢:
通常这个宏位于ProjectConfig.mk中,具体路径各不相同,如果找不到的话可以find -name “ProjectConfig.mk”搜索,另外有的同学改了以后还是无效,那可能不止这一处的mk文件定义了MTK_KERNEL_POWER_OFF_CHARGING哦,最简单的办法是直接全局搜索:
find -name “*.mk” | xargs grep “MTK_KERNEL_POWER_OFF_CHARGING = yes”
然后全部改为MTK_KERNEL_POWER_OFF_CHARGING = no

改完以后重新编译LK
mmm bootable/bootloader/lk:clean-lk
mmm bootable/bootloader/lk:lk
然后make snod 并 Download
USB插入,通电

Perfect!

总结

① 对于MTK平台,关键在于MTK_KERNEL_POWER_OFF_CHARGING这个宏,搜索源代码目录下的所有mk文件:
find -name “*.mk” | xargs grep “MTK_KERNEL_POWER_OFF_CHARGING = yes”
将其替换为为MTK_KERNEL_POWER_OFF_CHARGING = no即可

② 如果是MTK平台,但是没有MTK_KERNEL_POWER_OFF_CHARGING这个宏,尝试找到bootable\bootloader\lk\platform\mt6582\mt_kernel_power_off_charging.c文件
你的源码不一定是这个路径,尝试搜索mt_kernel_power_off_charging.c
find -name “mt_kernel_power_off_charging.c”
里面有个kernel_power_off_charging_detection(void)函数,函数内对g_boot_mode的赋值,通通改为g_boot_mode = NORMAL_BOOT;

③ 对于高通等其它平台,如果没有方法②中的函数,需要花点功夫了,首先定位bootloader的lk部分代码,然后搜索这些和启动模式相关的关键词:
g_boot_mode NORMAL_BOOT FASTBOOT RECOVERY_BOOT
搜索哪些地方对g_boot_mode赋值了:
find -name “*.c” | xargs grep “g_boot_mode = ”
着重去分析这些地方,配合打LOG调试,相信你很快就能搞定



本文由CharlesSimonyi发表于CSDN博客:https://blog.csdn.net/CharlesSimonyi/article/details/82236494 转载请注明出处

猜你喜欢

转载自blog.csdn.net/CharlesSimonyi/article/details/82236494