解析目的,使用天嵌开发板,发现用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来到内核
__reboot在arm架构的实现是这样的(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_reboot的syscall函数映射,即
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
- /**
- * Reboot the device. Will not return if the reboot is
- * successful. Requires the {@link android.Manifest.permission#REBOOT}
- * permission.
- *
- * @param reason code to pass to the kernel (e.g., "recovery") to
- * request special boot modes, or null.
- */
- public void reboot(String reason)
- {
- try {
- mService.reboot(reason);
- } catch (RemoteException e) {
- }
- }
mService为IPowerManagerBinder接口服务。
- /**
- * {@hide}
- */
- public PowerManager(IPowerManager service, Handler handler)
- {
- mService = service;
- mHandler = handler;
- }
2.frameworks/base/core/java/android/os/IPowerManager.aidl
- interface IPowerManager
- {
- ...
- void reboot(String reason);
- ...
- }
3.frameworks/base/services/java/com/android/server/PowerManagerService.java
- /**
- * Reboot the device immediately, passing 'reason' (may be null)
- * to the underlying __reboot system call. Should not return.
- */
- public void reboot(String reason)
- {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
- if (mHandler == null || !ActivityManagerNative.isSystemReady()) {
- throw new IllegalStateException("Too early to call reboot()");
- }
- final String finalReason = reason;
- Runnable runnable = new Runnable() {
- public void run() {
- synchronized (this) {
- ShutdownThread.reboot(getUiContext(), finalReason, false);
- }
- }
- };
- // ShutdownThread must run on a looper capable of displaying the UI.
- mHandler.post(runnable);
- // PowerManager.reboot() is documented not to return so just wait for the inevitable.
- synchronized (runnable) {
- while (true) {
- try {
- runnable.wait();
- } catch (InterruptedException e) {
- }
- }
- }
- }
4.frameworks/base/services/java/com/android/server/pm/ShutdownThread.java
- /**
- * Request a clean shutdown, waiting for subsystems to clean up their
- * state etc. Must be called from a Looper thread in which its UI
- * is shown.
- *
- * @param context Context used to display the shutdown progress dialog.
- * @param reason code to pass to the kernel (e.g. "recovery"), or null.
- * @param confirm true if user confirmation is needed before shutting down.
- */
- public static void reboot(final Context context, String reason, boolean confirm) {
- mReboot = true;
- mRebootSafeMode = false;
- mRebootReason = reason;
- shutdownInner(context, confirm);
- }
这里说明是需要重启,且不是安全模式,重启参数为传递下来的reason,shutdownInner的confirm参数是用来设置是否有确认提示框的,通过reboot接口调用重启是没有的,为false。
重启的实现在run()中,因为ShutdownThread是Thread的扩展,所以run会自动运行。
- /**
- * Makes sure we handle the shutdown gracefully.
- * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
- */
- public void run() {
- BroadcastReceiver br = new BroadcastReceiver() {
- @Override public void onReceive(Context context, Intent intent) {
- // We don't allow apps to cancel this, so ignore the result.
- actionDone();
- }
- };
- /*
- * Write a system property in case the system_server reboots before we
- * get to the actual hardware restart. If that happens, we'll retry at
- * the beginning of the SystemServer startup.
- */
- {
- String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
- SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
- }
- /*
- * If we are rebooting into safe mode, write a system property
- * indicating so.
- */
- if (mRebootSafeMode) {
- SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
- }
- ...
- rebootOrShutdown(mReboot, mRebootReason);
- }
在重启前会将重启原因写入sys.shutdown.requested,如果没有则为空,如果是安全模式还会将persist.sys.safemode置1,之后会进行一些关机前的预处理,关闭ActivityManager以及MountService,最终调用rebootOrShutdown进行关机操作。
- /**
- * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
- * or {@link #shutdown(Context, boolean)} instead.
- *
- * @param reboot true to reboot or false to shutdown
- * @param reason reason for reboot
- */
- public static void rebootOrShutdown(boolean reboot, String reason) {
- if (reboot) {
- Log.i(TAG, "Rebooting, reason: " + reason);
- try {
- PowerManagerService.lowLevelReboot(reason);
- } catch (Exception e) {
- Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
- }
- } else if (SHUTDOWN_VIBRATE_MS > 0) {
- // vibrate before shutting down
- Vibrator vibrator = new SystemVibrator();
- try {
- vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
- } catch (Exception e) {
- // Failure to vibrate shouldn't interrupt shutdown. Just log it.
- Log.w(TAG, "Failed to vibrate during shutdown.", e);
- }
- // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
- try {
- Thread.sleep(SHUTDOWN_VIBRATE_MS);
- } catch (InterruptedException unused) {
- }
- }
- // Shutdown power
- Log.i(TAG, "Performing low-level shutdown...");
- PowerManagerService.lowLevelShutdown();
- }
- }
如果确认重启,则调用PowerManagerService的lowLevelReboot函数,参数就是传递下来的reason,稍后分析。如果不是重启,即mReboot=false,那就是需要关机了,在shutdown函数中就能够知道。
- /**
- * Request a clean shutdown, waiting for subsystems to clean up their
- * state etc. Must be called from a Looper thread in which its UI
- * is shown.
- *
- * @param context Context used to display the shutdown progress dialog.
- * @param confirm true if user confirmation is needed before shutting down.
- */
- public static void shutdown(final Context context, boolean confirm) {
- mReboot = false;
- mRebootSafeMode = false;
- shutdownInner(context, confirm);
- }
关机的时候需要震动,就是这里了SHUTDOWN_VIBRATE_MS,默认的定义是500ms。但是在代码上看,无论如何,最后都会调用一下lowLevelShutdown函数,也就是关机。逻辑上,这里可能是个问题,但是实际中,如果重启操作能够调用成功的话,整个系统都重启了,后边的代码当然不可能执行到了。
目光转回PowerManagerService
5.frameworks/base/services/java/com/android/server/PowerManagerService.java
- /**
- * Low-level function to reboot the device.
- *
- * @param reason code to pass to the kernel (e.g. "recovery"), or null.
- * @throws IOException if reboot fails for some reason (eg, lack of
- * permission)
- */
- public static void lowLevelReboot(String reason) throws IOException {
- nativeReboot(reason);
- }
- /**
- * Low-level function turn the device off immediately, without trying
- * to be clean. Most people should use
- * {@link com.android.server.pm.internal.app.ShutdownThread} for a clean shutdown.
- */
- public static void lowLevelShutdown() {
- nativeShutdown();
- }
很熟悉的字样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
- static JNINativeMethod gPowerManagerServiceMethods[] = {
- /* name, signature, funcPtr */
- ...
- { "nativeShutdown", "()V",
- (void*) nativeShutdown },
- { "nativeReboot", "(Ljava/lang/String;)V",
- (void*) nativeReboot },
- ...
- };
- static void nativeShutdown(JNIEnv *env, jobject clazz) {
- android_reboot(ANDROID_RB_POWEROFF, 0, 0);
- }
- static void nativeReboot(JNIEnv *env, jobject clazz, jstring reason) {
- if (reason == NULL) {
- android_reboot(ANDROID_RB_RESTART, 0, 0);
- } else {
- const char *chars = env->GetStringUTFChars(reason, NULL);
- android_reboot(ANDROID_RB_RESTART2, 0, (char *) chars);
- env->ReleaseStringUTFChars(reason, chars); // In case it fails.
- }
- jniThrowIOException(env, errno);
- }
关机或者重启。
7.system/core/libcutils/android_reboot.c
- int android_reboot(int cmd, int flags, char *arg)
- {
- int ret = 0;
- int reason = -1;
- #ifdef RECOVERY_PRE_COMMAND
- if (cmd == (int) ANDROID_RB_RESTART2) {
- if (arg && strlen(arg) > 0) {
- char cmd[PATH_MAX];
- sprintf(cmd, RECOVERY_PRE_COMMAND " %s", arg);
- system(cmd);
- }
- }
- #endif
- if (!(flags & ANDROID_RB_FLAG_NO_SYNC))
- sync();
- if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))
- remount_ro();
- switch (cmd) {
- case ANDROID_RB_RESTART:
- reason = RB_AUTOBOOT;
- break;
- case ANDROID_RB_POWEROFF:
- ret = reboot(RB_POWER_OFF);
- return ret;
- case ANDROID_RB_RESTART2:
- // REBOOT_MAGIC
- break;
- default:
- return -1;
- }
- #ifdef RECOVERY_PRE_COMMAND_CLEAR_REASON
- reason = RB_AUTOBOOT;
- #endif
- if (reason != -1)
- ret = reboot(reason);
- else
- ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
- LINUX_REBOOT_CMD_RESTART2, arg);
- return ret;
- }
以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中:
- #define RB_AUTOBOOT LINUX_REBOOT_CMD_RESTART
- #define RB_HALT_SYSTEM LINUX_REBOOT_CMD_HALT
- #define RB_ENABLE_CAD LINUX_REBOOT_CMD_CAD_ON
- #define RB_DISABLE_CAD LINUX_REBOOT_CMD_CAD_OFF
- #define RB_POWER_OFF LINUX_REBOOT_CMD_POWER_OFF
8.bionic/libc/unistd/reboot.c
- int reboot (int mode)
- {
- return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL );
- }