Android fastboot下载模式或reboot流程解析

解析目的,使用天嵌开发板,发现用adbreboot bootloader机器没有进入uboot fastboot模式下而是直接重启;经发现,天嵌不是使用reboot bootloader,而是使用rebootfastboot。

 

因此我们从底层往上次分析(uboot -kernel - Android)。

深入分析:

 

uboot:

代码分析:

uboot-imx\lib_arm\board.c

void start_armboot (void)   uboot入口函数

{

-----------

#ifdef CONFIG_FASTBOOT

         check_fastboot_mode();

#endif

         /*main_loop() can return to retry autoboot, if so just run it again. */

         for(;;) {

                   main_loop();

         }

----------

}

 

uboot-imx\drivers\fastboot\ fastboot.c

void check_fastboot_mode(void)

{

         if(fastboot_check_and_clean_flag())

                   do_fastboot(NULL,0, 0, 0);

}

 

uboot-imx\cpu\arm_cortexa8\mx6\generic.c

int fastboot_check_and_clean_flag(void)

{

         intflag_set = 0;

         u32reg;

 

         reg= readl(SNVS_BASE_ADDR + SNVS_LPGPR);

 

         flag_set= !!(reg & ANDROID_FASTBOOT_BOOT);

 

         /*clean it in case looping infinite here.... */

         if(flag_set) {

                   reg&= ~ANDROID_FASTBOOT_BOOT;

                   writel(reg,SNVS_BASE_ADDR + SNVS_LPGPR);

         }

 

         returnflag_set;

}

 

uboot-imx\include\asm-arm\arch-mx6\mx6.h

MX6Q_SNVS_BASE_ADDR   = 0x02000000 + 0x4C000

SNVS_LPGPR = 0x68

 

fastboot_check_and_clean_flag函数主要是从寄存器中读取标志位,以判断是否需要进入fastboot模式。该标志位在kernel中进行设置,在下一步会进行解析。

 

我回到do_fastboot(NULL, 0,0, 0);函数,该函数直接调用fastboot服务。

uboot-imx\common\cmd_fastboot.c fastboot服务具体做的工作就是下载烧写,具体就不再这里描述了。

 

注意:这里必须开启fastboot下载功能,宏定义位于:uboot-imx\include\configs\ mx6q_sabresd_android.h (CONFIG_FASTBOOT)

 

kernel:

1.reboot入口

__reboot通过syscall来到内核

__rebootarm架构的实现是这样的(bionic/libc/arch-arm/syscalls/__reboot.S:

 

ENTRY(__reboot)

    .save   {r4, r7} 

    stmfd   sp!, {r4, r7}

    ldr     r7, =__NR_reboot

    swi     #0  

    ldmfd   sp!, {r4, r7}

    movs    r0, r0

    bxpl    lr  

    b       __set_syscall_errno

END(__reboot)

 

2.reboot实现入口

__NR_reboot实现部分:

 

kernel_imx\arch\arm\include\asm\unistd.h

#define __NR_swapon                       (__NR_SYSCALL_BASE+ 87)

#define __NR_reboot                         (__NR_SYSCALL_BASE+ 88)

 

其被指定了一个固定的偏移量,在被调用的时候就是通过这个偏移量去内核中寻找对应的入口的,由此可见,内核中一定有着相同的定义,否则将不能成功调用。内核中对syscall偏移量的定义在内核源码中的arch/arm/include/asm/unistd.h,相关信息完全一致。

 

已经找到了内核中的对应映射,那么下一步就要去找寻真正的实现函数了,在include/asm-generic/unistd.h中可以找到内核对__NR_rebootsyscall函数映射,即

kernel_imx\include\asm-generic\unistd.h

 

/* kernel/sys.c */

#define __NR_setpriority 140

__SYSCALL(__NR_setpriority,sys_setpriority)

#define __NR_getpriority 141

__SYSCALL(__NR_getpriority, sys_getpriority)

#define __NR_reboot 142

__SYSCALL(__NR_reboot, sys_reboot)

 

kernel_imx\kernel\sys.c 入口函数

SYSCALL_DEFINE4(reboot, int, magic1, int,magic2, unsigned int, cmd,void __user *, arg)

 

通过kernel_imx\include\linux\syscalls.h宏定义解析之后呈现函数为:

asmlinkage long sys_reboot(intmagic1, int magic2, unsigned int cmd,void __user *arg);

具体实现:

#define SYSCALL_DEFINE4(name, ...)SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)

----------------

#define SYSCALL_DEFINEx(x, sname, ...)                                     \

----------------------

__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

---------------------

 

#define __SYSCALL_DEFINEx(x, name, ...)

----------------------

asmlinkage longsys##name(__SC_DECL##x(__VA_ARGS__))

 

 

回到SYSCALL_DEFINE4函数调用kernel_restart(buffer);

void kernel_restart(char *cmd)

{

         kernel_restart_prepare(cmd);

         if(!cmd)

                   printk(KERN_EMERG"Restarting system.\n");

         else

                   printk(KERN_EMERG"Restarting system with command '%s'.\n", cmd);

         kmsg_dump(KMSG_DUMP_RESTART);

         machine_restart(cmd);

}

 

kernel_imx\arch\arm\kernel\process.c

void machine_restart(char *cmd)

{

         machine_shutdown();

         arm_pm_restart(reboot_mode,cmd);

}

 

定义指针函数

void (*arm_pm_restart)(char str, const char*cmd) = arm_machine_restart;

EXPORT_SYMBOL_GPL(arm_pm_restart);

 

 

void arm_machine_restart(char mode, constchar *cmd)

{

         /*Flush the console to make sure all the relevant messages make it

          * out to the console drivers */

         arm_machine_flush_console();

 

         /*Disable interrupts first */

         local_irq_disable();

         local_fiq_disable();

 

         /*

          * Tell the mm system that we are going toreboot -

          * we may need it to insert some 1:1 mappingsso that

          * soft boot works.

          */

         setup_mm_for_reboot(mode);

 

         /*Clean and invalidate caches */

         flush_cache_all();

 

         /*Turn off caching */

         cpu_proc_fin();

 

         /*Push out any further dirty data, and ensure cache is empty */

         flush_cache_all();

 

         /*

          * Now call the architecture specific rebootcode.

          */

         arch_reset(mode,cmd);

 

         /*

          * Whoops - the architecture was unable toreboot.

          * Tell the user!

          */

         mdelay(1000);

         printk("Rebootfailed -- System halted\n");

         while(1);

}

 

arch_reset  函数为rebootBootLoader实现的最后位置,这里红色部分fastboot其实标准应该为bootLoader。cpu_reset(0);该函数跳转到机器地址起始位,执行重启。

 

kernel_imx\arch\arm\plat-mxc\system.c

static void arch_reset_special_mode(charmode, const char *cmd)

{

         if(strcmp(cmd, "download") == 0)

                   do_switch_mfgmode();

         elseif (strcmp(cmd, "recovery") == 0)

                   do_switch_recovery();

         elseif (strcmp(cmd, "fastboot") == 0)

                   do_switch_fastboot();

}

 

void arch_reset(char mode, const char *cmd)

{

         unsignedint wcr_enable;

         arch_reset_special_mode(mode,cmd);

    --------------------------------------

         if(cpu_is_mx1()) {

                   wcr_enable= (1 << 0);

         }else {

                   structclk *clk;

 

                   clk= clk_get_sys("imx2-wdt.0", NULL);

                   if(!IS_ERR(clk))

                            clk_enable(clk);

                   wcr_enable= (1 << 2);

         }

 

         /*Assert SRS signal */

         __raw_writew(wcr_enable,wdog_base);

 

         /*wait for reset to assert... */

         mdelay(500);

 

         printk(KERN_ERR"Watchdog reset failed to assert reset\n");

 

         /*delay to allow the serial port to show the message */

         mdelay(50);

 

         /*we'll take a jump through zero as a poor second */

         cpu_reset(0);

}

 

fastboot标志写入寄存器中。

kernel_imx\arch\arm\mach-mx6\system.c

void do_switch_fastboot(void)

{

         u32reg;

 

         reg= __raw_readl(MX6Q_SNVS_BASE_ADDR + SNVS_LPGPR);

         reg|= ANDROID_FASTBOOT_BOOT;

         __raw_writel(reg,MX6Q_SNVS_BASE_ADDR + SNVS_LPGPR);

}

 

arch_reset函数操作完后直接重启机器。

 

 

Android:

该层与安卓系统通用,这里引用网络解析。

1.frameworks/base/core/java/android/os/PowerManager.java

  1. /**  
  2.  * Reboot the device.  Will not return if the reboot is 
  3.  * successful.  Requires the {@link android.Manifest.permission#REBOOT} 
  4.  * permission. 
  5.  * 
  6.  * @param reason code to pass to the kernel (e.g., "recovery") to 
  7.  *               request special boot modes, or null. 
  8.  */  
  9. public void reboot(String reason)  
  10. {     
  11.     try {  
  12.         mService.reboot(reason);  
  13.     } catch (RemoteException e) {  
  14.     }     

 

mServiceIPowerManagerBinder接口服务。

 

  1. /** 
  2.  * {@hide} 
  3.  */  
  4. public PowerManager(IPowerManager service, Handler handler)  
  5. {  
  6.     mService = service;  
  7.     mHandler = handler;  
  8. }  

 

2.frameworks/base/core/java/android/os/IPowerManager.aidl

  1. interface IPowerManager  
  2. {  
  3. ...  
  4. void reboot(String reason);  
  5. ...  
  6. }  

3.frameworks/base/services/java/com/android/server/PowerManagerService.java

  1. /**   
  2.  * Reboot the device immediately, passing 'reason' (may be null) 
  3.  * to the underlying __reboot system call.  Should not return. 
  4.  */  
  5. public void reboot(String reason)  
  6. {      
  7.     mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);  
  8.   
  9.     if (mHandler == null || !ActivityManagerNative.isSystemReady()) {  
  10.         throw new IllegalStateException("Too early to call reboot()");  
  11.     }      
  12.   
  13.     final String finalReason = reason;  
  14.     Runnable runnable = new Runnable() {  
  15.         public void run() {  
  16.             synchronized (this) {  
  17.                 ShutdownThread.reboot(getUiContext(), finalReason, false);  
  18.             }      
  19.   
  20.         }      
  21.     };     
  22.     // ShutdownThread must run on a looper capable of displaying the UI.  
  23.     mHandler.post(runnable);  
  24.   
  25.     // PowerManager.reboot() is documented not to return so just wait for the inevitable.  
  26.     synchronized (runnable) {  
  27.         while (true) {  
  28.             try {  
  29.                 runnable.wait();  
  30.             } catch (InterruptedException e) {   
  31.             }      
  32.         }      
  33.     }      
  34. }  

4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java

  1. /** 
  2.  * Request a clean shutdown, waiting for subsystems to clean up their 
  3.  * state etc.  Must be called from a Looper thread in which its UI 
  4.  * is shown. 
  5.  * 
  6.  * @param context Context used to display the shutdown progress dialog. 
  7.  * @param reason code to pass to the kernel (e.g. "recovery"), or null. 
  8.  * @param confirm true if user confirmation is needed before shutting down. 
  9.  */  
  10. public static void reboot(final Context context, String reason, boolean confirm) {  
  11.     mReboot = true;  
  12.     mRebootSafeMode = false;  
  13.     mRebootReason = reason;  
  14.     shutdownInner(context, confirm);  
  15. }  


这里说明是需要重启,且不是安全模式,重启参数为传递下来的reason,shutdownInner的confirm参数是用来设置是否有确认提示框的,通过reboot接口调用重启是没有的,为false。

重启的实现在run()中,因为ShutdownThreadThread的扩展,所以run会自动运行。

  1. /** 
  2.  * Makes sure we handle the shutdown gracefully. 
  3.  * Shuts off power regardless of radio and bluetooth state if the alloted time has passed. 
  4.  */   
  5. public void run() {  
  6.     BroadcastReceiver br = new BroadcastReceiver() {  
  7.         @Override public void onReceive(Context context, Intent intent) {  
  8.             // We don't allow apps to cancel this, so ignore the result.  
  9.             actionDone();  
  10.         }  
  11.     };  
  12.   
  13.     /* 
  14.      * Write a system property in case the system_server reboots before we 
  15.      * get to the actual hardware restart. If that happens, we'll retry at 
  16.      * the beginning of the SystemServer startup. 
  17.      */   
  18.     {     
  19.         String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");  
  20.         SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);  
  21.     }  
  22.   
  23.     /* 
  24.      * If we are rebooting into safe mode, write a system property 
  25.      * indicating so. 
  26.      */   
  27.     if (mRebootSafeMode) {  
  28.         SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");  
  29.     }  
  30.     ...  
  31.     rebootOrShutdown(mReboot, mRebootReason);  
  32. }  

 

在重启前会将重启原因写入sys.shutdown.requested,如果没有则为空,如果是安全模式还会将persist.sys.safemode1,之后会进行一些关机前的预处理,关闭ActivityManager以及MountService,最终调用rebootOrShutdown进行关机操作。

  1. /** 
  2.      * Do not call this directly. Use {@link #reboot(Context, String, boolean)} 
  3.      * or {@link #shutdown(Context, boolean)} instead. 
  4.      * 
  5.      * @param reboot true to reboot or false to shutdown 
  6.      * @param reason reason for reboot 
  7.      */  
  8.     public static void rebootOrShutdown(boolean reboot, String reason) {  
  9.         if (reboot) {  
  10.             Log.i(TAG, "Rebooting, reason: " + reason);   
  11.             try {  
  12.                 PowerManagerService.lowLevelReboot(reason);  
  13.             } catch (Exception e) {  
  14.                 Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);  
  15.             }   
  16.         } else if (SHUTDOWN_VIBRATE_MS > 0) {  
  17.             // vibrate before shutting down  
  18.             Vibrator vibrator = new SystemVibrator();  
  19.             try {  
  20.                 vibrator.vibrate(SHUTDOWN_VIBRATE_MS);  
  21.             } catch (Exception e) {  
  22.                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.  
  23.                 Log.w(TAG, "Failed to vibrate during shutdown.", e);  
  24.             }     
  25.                   
  26.             // vibrator is asynchronous so we need to wait to avoid shutting down too soon.  
  27.             try {  
  28.                 Thread.sleep(SHUTDOWN_VIBRATE_MS);  
  29.             } catch (InterruptedException unused) {  
  30.             }     
  31.         }     
  32.               
  33.         // Shutdown power  
  34.         Log.i(TAG, "Performing low-level shutdown...");  
  35.         PowerManagerService.lowLevelShutdown();  
  36.     }  
  37. }  


如果确认重启,则调用PowerManagerService的lowLevelReboot函数,参数就是传递下来的reason,稍后分析。如果不是重启,即mReboot=false,那就是需要关机了,在shutdown函数中就能够知道。

  1. /** 
  2.  * Request a clean shutdown, waiting for subsystems to clean up their 
  3.  * state etc.  Must be called from a Looper thread in which its UI 
  4.  * is shown. 
  5.  * 
  6.  * @param context Context used to display the shutdown progress dialog. 
  7.  * @param confirm true if user confirmation is needed before shutting down. 
  8.  */  
  9. public static void shutdown(final Context context, boolean confirm) {  
  10.     mReboot = false;  
  11.     mRebootSafeMode = false;  
  12.     shutdownInner(context, confirm);  
  13. }  


关机的时候需要震动,就是这里了SHUTDOWN_VIBRATE_MS,默认的定义是500ms。但是在代码上看,无论如何,最后都会调用一下lowLevelShutdown函数,也就是关机。逻辑上,这里可能是个问题,但是实际中,如果重启操作能够调用成功的话,整个系统都重启了,后边的代码当然不可能执行到了。

目光转回PowerManagerService

 

5.frameworks/base/services/java/com/android/server/PowerManagerService.java

  1. /**   
  2.  * Low-level function to reboot the device. 
  3.  * 
  4.  * @param reason code to pass to the kernel (e.g. "recovery"), or null. 
  5.  * @throws IOException if reboot fails for some reason (eg, lack of 
  6.  *         permission) 
  7.  */  
  8. public static void lowLevelReboot(String reason) throws IOException {  
  9.     nativeReboot(reason);  
  10. }    
  11.   
  12. /**   
  13.  * Low-level function turn the device off immediately, without trying 
  14.  * to be clean.  Most people should use 
  15.  * {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown. 
  16.  */  
  17. public static void lowLevelShutdown() {  
  18.     nativeShutdown();  
  19. }   

 

很熟悉的字样native,是JNI调用了:

1.  private static native void nativeShutdown();  

2.  private static native void nativeReboot(String reason) throws IOException;  


6.frameworks/base/services/jni/com_android_server_PowerManagerService.cpp

  1. static JNINativeMethod gPowerManagerServiceMethods[] = {   
  2.     /* name, signature, funcPtr */  
  3.     ...  
  4.     { "nativeShutdown""()V",  
  5.             (void*) nativeShutdown },  
  6.     { "nativeReboot""(Ljava/lang/String;)V",  
  7.             (void*) nativeReboot },  
  8.     ...  
  9. };  

 

  1. static void nativeShutdown(JNIEnv *env, jobject clazz) {  
  2.     android_reboot(ANDROID_RB_POWEROFF, 0, 0);  
  3. }  
  4.   
  5. static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {  
  6.     if (reason == NULL) {  
  7.         android_reboot(ANDROID_RB_RESTART, 0, 0);  
  8.     } else {  
  9.         const char *chars = env->GetStringUTFChars(reason, NULL);  
  10.         android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);  
  11.         env->ReleaseStringUTFChars(reason, chars);  // In case it fails.  
  12.     }  
  13.     jniThrowIOException(env, errno);  
  14. }  


关机或者重启。

7.system/core/libcutils/android_reboot.c

  1. int android_reboot(int cmd, int flags, char *arg)  
  2. {  
  3.     int ret = 0;  
  4.     int reason = -1;  
  5.   
  6. #ifdef RECOVERY_PRE_COMMAND  
  7.     if (cmd == (int) ANDROID_RB_RESTART2) {  
  8.         if (arg && strlen(arg) > 0) {  
  9.             char cmd[PATH_MAX];  
  10.             sprintf(cmd, RECOVERY_PRE_COMMAND " %s", arg);  
  11.             system(cmd);  
  12.         }  
  13.     }  
  14. #endif  
  15.   
  16.     if (!(flags & ANDROID_RB_FLAG_NO_SYNC))  
  17.         sync();  
  18.   
  19.     if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))  
  20.         remount_ro();  
  21.   
  22.     switch (cmd) {  
  23.         case ANDROID_RB_RESTART:  
  24.             reason = RB_AUTOBOOT;  
  25.             break;  
  26.   
  27.         case ANDROID_RB_POWEROFF:  
  28.             ret = reboot(RB_POWER_OFF);  
  29.             return ret;  
  30.   
  31.         case ANDROID_RB_RESTART2:  
  32.             // REBOOT_MAGIC  
  33.             break;  
  34.   
  35.         default:  
  36.             return -1;  
  37.     }  
  38.   
  39. #ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON  
  40.     reason = RB_AUTOBOOT;  
  41. #endif  
  42.   
  43.     if (reason != -1)  
  44.         ret = reboot(reason);  
  45.     else  
  46.         ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,  
  47.                            LINUX_REBOOT_CMD_RESTART2, arg);  
  48.   
  49.     return ret;  
  50. }  


reboot recovery为例,arg即为recovery,所在在第六步的时候会传入ANDROID_RB_RESTART2。到了android_reboot函数中,会看到这样的定义#ifdefRECOVERY_PRE_COMMAND,即属于重启前会执行的命令,如果定义了就会执行。

下面也是做了一些关机重启前的预处理工作,sync()作用是将缓存中的信息写入磁盘,以免程序异常结束导致文件被损坏,linux系统关机前会做几次这样的动作;而remount_ro()作用是通过调用emergency_remount()强制将文件系统挂载为只读,不再允许任何写入操作,同时会通过检查/proc/mounts的设备状态来确认是否当前的所有写入工作已经完成,这个检查过程是阻塞操作。

接下来才是对参数的解析处理:

1)普通重启ANDROID_RB_RESTART reason = RB_AUTOBOOT

2)关机ANDROID_RB_POWEROFF无需reason,直接调用reboot进行关机;

3)带参数的特殊重启ANDROID_RB_RESTART2 reason 将为默认值-1

这里又出现一个#ifdefRECOVERY_PRE_COMMAND_CLEAR_REASON,如果定义了它,则无论上层传下来的参数是什么样的,最终都只是普通重启而已。定义它的方式是在BoardConfig.mk中加入TARGET_RECOVERY_PRE_COMMAND_CLEAR_REASON:= true,应该有厂商会喜欢这么做的,毕竟除了普通重启,都可能带给用户一定的风险。

最后会对reason进行一个检测,那么通过上边的分析,其实只有带参数的特殊重启才会为-1,而不等于-1的情况中有普通重启和关机,而关机已经自行解决了……所以,不等于-1的情况到了这里也只有普通重启了。最终这里就是区分普通重启与特殊重启的地方了。这里再插入一个问题,其他的几个cmd都是什么值呢?答案在bionic/libc/include/sys/reboot.h中:

  1. #define RB_AUTOBOOT     LINUX_REBOOT_CMD_RESTART  
  2. #define RB_HALT_SYSTEM  LINUX_REBOOT_CMD_HALT  
  3. #define RB_ENABLE_CAD   LINUX_REBOOT_CMD_CAD_ON  
  4. #define RB_DISABLE_CAD  LINUX_REBOOT_CMD_CAD_OFF  
  5. #define RB_POWER_OFF    LINUX_REBOOT_CMD_POWER_OFF 


8.bionic/libc/unistd/reboot.c

  1. int reboot (int  mode)   
  2. {  
  3.     return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );  

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值