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

2019-04-13 21:05发布

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

总所周知,一台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代码中搜索,进一步定位关键代码位置,通过阅读代码,bootableootloaderlkplatformmt6582oot_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 "); 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 ",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 "); printf("MT65XX_FACTORY_KEY 0x%x ",MT65XX_FACTORY_KEY); printf("MT65XX_BOOT_MENU_KEY 0x%x ",MT65XX_BOOT_MENU_KEY); printf("MT65XX_RECOVERY_KEY 0x%x ",MT65XX_RECOVERY_KEY); while(get_timer(begin)<50) { if(!factory_forbidden){ if(mtk_detect_key(MT65XX_FACTORY_KEY)) { printf("%s Detect key ",MODULE_NAME); printf("%s Enable factory mode ",MODULE_NAME); g_boot_mode = FACTORY_BOOT; //video_printf("%s : detect factory mode ! ",MODULE_NAME); return; } } if(mtk_detect_key(MT65XX_BOOT_MENU_KEY)) { printf(" %s Check boot menu ",MODULE_NAME); printf("%s Wait 50ms for special keys ",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 ",MODULE_NAME); printf("%s Enable recovery mode ",MODULE_NAME); g_boot_mode = RECOVERY_BOOT; //video_printf("%s : detect recovery mode ! ",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 "); 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 ",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 "); g_boot_mode = FASTBOOT; return ; } #endif if(!factory_forbidden){ if(mtk_detect_key(MT65XX_FACTORY_KEY)) { printf("%s Detect key ",MODULE_NAME); printf("%s Enable factory mode ",MODULE_NAME); g_boot_mode = FACTORY_BOOT; //video_printf("%s : detect factory mode ! ",MODULE_NAME); return ; } } #ifdef MT65XX_RECOVERY_KEY if(mtk_detect_key(MT65XX_RECOVERY_KEY)) { printf("%s Detect cal key ",MODULE_NAME); printf("%s Enable recovery mode ",MODULE_NAME); g_boot_mode = RECOVERY_BOOT; //video_printf("%s : detect recovery mode ! ",MODULE_NAME); return ; } #endif } #endif #ifdef MTK_KERNEL_POWER_OFF_CHARGING if(kernel_power_off_charging_detection()) { printf(" < Kernel Power Off Charging Detection Ok> "); return; } else { printf("< Kernel Enter Normal Boot > "); } #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> "); return; } else { printf("< Kernel Enter Normal Boot > "); } #endif 看到了power_off_charging,似乎和关机状态下的充电有关系,再看两个分支的printf,一个分支和充电相关,一个分支是Kernel Enter Normal Boot正常开机启动。马上搜索kernel_power_off_charging_detection的定义:
find -name “*.c” | xargs grep “kernel_power_off_charging_detection”
在bootableootloaderlkplatformmt6582mt_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 ", __func__); return FALSE; } off_mode_status = get_off_mode_charge_status(); printf("[%s] off_mode_status %d ", __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 "); 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 = ”
可以看到,只有bootableootloaderlkplatformmt6582mt_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> "); return; } else { printf("< Kernel Enter Normal Boot > "); } #endif 在bootableootloaderlkplatformmt6582oot_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这个宏,尝试找到bootableootloaderlkplatformmt6582mt_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 转载请注明出处