转载请标注原文地址:http://blog.csdn.net/uranus_wm/article/details/10542715
这里说的reboot是指software restart,常用场景有两种:
1.android应用层出现关键进程挂起,如system进程挂起引起的reboot
2.直接通过tty控制台输入reboot
以android4.0.4版本,samsung-exynos-4412+s5p8767(PMU)为例:
Android部分:
文件位置:/frameworks/base/core/java/com/android/internal/app/ShutdownThread.java
文件中有一个单例模式class ShutdownThread,包含reboot和shutdown两个静态方法。
public final class ShutdownThread extends Thread { /** * 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; mRebootReason = reason; shutdown(context, confirm); } }
通过调用内部方法rebootOrShutdown会再调用到静态类Power.java的reboot方法
文件位置:/frameworks/base/core/java/android/os/power.java
/** * 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 reboot(String reason) throws IOException { rebootNative(reason); } private static native void rebootNative(String reason) throws IOException ;
通过jni调用,一层层跟踪发现最终调用到
文件位置: /bionic/libc/unistd/reboot.c
int reboot (int mode) { return __reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL ); }
进一步通过文件位置:/bionic/libc/arch-sh/syscalls/__reboot.S 调用系统调用__NR_reboot,其系统调用号是 88
转载请标注原文地址:http://blog.csdn.net/uranus_wm/article/details/10542715
文件位置:/bionic/libc/include/sys/linux-syscalls.h
#define __NR_umask (__NR_SYSCALL_BASE + 60) #define __NR_reboot (__NR_SYSCALL_BASE + 88) #define __NR_syslog (__NR_SYSCALL_BASE + 103)
到Kernel部分:
文件位置:/kernel/arch/arm/include/asm 有一个对应的系统调用号,reboot当然也是88
#define __NR_swapon (__NR_SYSCALL_BASE+ 87) #define __NR_reboot (__NR_SYSCALL_BASE+ 88) #define __NR_readdir (__NR_SYSCALL_BASE+ 89)
文件位置:/kernel/kernel/sys.c 定义了reboot的函数实现
/* * Reboot system call: for obvious reasons only root may call it, * and even root needs to set up some magic numbers in the registers * so that some mistake won't make this reboot the whole machine. * You can also set the meaning of the ctrl-alt-del-key here. * * reboot doesn't sync: do that yourself before calling this. */ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { /* We only trust the superuser with rebooting the system. */ if (!capable(CAP_SYS_BOOT)) return -EPERM; /* For safety, we require "magic" arguments. */ if (magic1 != LINUX_REBOOT_MAGIC1 || (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && magic2 != LINUX_REBOOT_MAGIC2B && magic2 != LINUX_REBOOT_MAGIC2C)) return -EINVAL; /* Instead of trying to make the power_off code look like * halt when pm_power_off is not set do it the easy way. */ if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off) cmd = LINUX_REBOOT_CMD_HALT; mutex_lock(&reboot_mutex); switch (cmd) { case LINUX_REBOOT_CMD_RESTART: kernel_restart(NULL); break; case LINUX_REBOOT_CMD_HALT: kernel_halt(); do_exit(0); panic("cannot halt"); case LINUX_REBOOT_CMD_POWER_OFF: kernel_power_off(); do_exit(0); break; } mutex_unlock(&reboot_mutex); }
然后我们就看到了kernel_restart(NULL);
文件位置:/kernel/kernel/sys.c
void kernel_restart_prepare(char *cmd) { blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; usermodehelper_disable(); device_shutdown(); syscore_shutdown(); } /** * kernel_restart - reboot the system * @cmd: pointer to buffer containing command to execute for restart * or %NULL * * Shutdown everything and perform a clean reboot. * This is not safe to call in interrupt context. */ 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); } EXPORT_SYMBOL_GPL(kernel_restart);
device_shutdown()通知devices_kset上的设备调用shutdown注册函数
syscore_shutdown()通知syscore_ops_list上的设备调用shutdown注册函数
跟踪下来这个list上只有一个设备注册了shutdown函数:
文件位置:/kernel/kernel/irq/generic-chip.c
static struct syscore_ops irq_gc_syscore_ops = { .suspend = irq_gc_suspend, .resume = irq_gc_resume, .shutdown = irq_gc_shutdown, };
回到kernel_restart(),下面调用的就是machine_restart();
这个函数和cpu平台有关,exynos4412平台的函数位置在
文件位置:/kernel/arch/arm/kernel/process.c
void machine_shutdown(void) { #ifdef CONFIG_SMP smp_send_stop(); #endif } void machine_restart(char *cmd) { machine_shutdown(); arm_pm_restart(reboot_mode, cmd); }
exynos4412是个四核cpu0~cpu3,smp_send_stop()就是通知cpu1,cpu2,cpu3停止运行
最后调用的是同文件的arm_machine_restart
void arm_machine_restart(char mode, const char *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 to reboot - * we may need it to insert some 1:1 mappings so 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 reboot code. */ arch_reset(mode, cmd); /* * Whoops - the architecture was unable to reboot. * Tell the user! */ mdelay(1000); printk("Reboot failed -- System halted\n"); while (1); }
幸运的是在arch_reset之前,printk都是有效的,如果调试中碰到log不能输出的问题,可以参考文章:使用printascii与printhex输出log到串口
原文地址:http://blog.csdn.net/uranus_wm/article/details/11176877
实际使用reboot命令reset时,发现cpu_proc_fin()这个函数执行不过去,不知道是不是我们开发板的问题,我注释掉才可以通过
最后面的arch_reset(mode, cmd)是最终的reset函数
文件位置:/kernel/arch/plat-s5p/reset.c
void arch_reset(char mode, const char *cmd) { /* SWRESET support in s5p_reset_hook() */ if (s5p_reset_hook) s5p_reset_hook(); /* Perform reset using Watchdog reset * if there is no s5p_reset_hook() */ arch_wdt_reset(); }
这里调用s5p_reset_hook()是通过设置cpu的专有寄存器SWRESET复位,
而后面的arch_wdt_reset()是通过设置watch_dog是手机复位,这一句实际上保险措施,实际不一定执行的到
文件位置:/kernel/arch/mach-exynos/cpu-exynos4.c
/* set sw_reset function */ s5p_reset_hook = exynos4_sw_reset; static void exynos4_sw_reset(void) { int count = 3; while (count--) { __raw_writel(0x1, S5P_SWRESET); mdelay(500); } }
S5P_SWRESET是exynos4412提供的专有寄存器,当该寄存器被设置后,系统会软件重启,重启之后可以通过查询RST_STAT寄存器获取cpu reset信息
启动过程:
1. reset之后cpu会和正常开机一样,从irom开始执行;和正常开机不同的是他没有power_key的上电过程,通过外部cmu启动给cpu上电。
2. irom内置一段小程序用于初始化soc,这段程序就是在irom上直接执行
3. load BL1,BL1包含一些芯片相关的配置,BL1存储在外部存储器,如EMMC上
4. load BL2,BL2包含一些平台相关的配置,BL2也存储在外部存储器上,如EMMC上
5. 前四点由于没有源码,仅能通过文档介绍了解,最后跳转到u-boot
文件位置:/arch/arm/cpu/armv7/start.S
.globl _start _start: b reset reset: bl cpu_init_crit
文件位置:/board/samsung/smdk4212/lowlevel_init.S
.globl lowlevel_init lowlevel_init: /* use iROM stack in bl2 */ ldr sp, =0x02060000 push {lr} /* check reset status */ ldr r0, =(INF_REG_BASE + INF_REG1_OFFSET) ldr r1, [r0] /* Sleep wakeup reset */ ldr r2, =S5P_CHECK_SLEEP cmp r1, r2 beq wakeup_reset /* During sleep/wakeup or AFTR mode, pmic_init function is not available * and it causes delays. So except for sleep/wakeup and AFTR mode, * the below function is needed */ bl pmic_init bl uart_asm_init bl read_om
这里会通过专有寄存器INF_REG1_OFFSET判断是wakeup还是reset,关于系统sleep和wakeup我会专门有一篇文章介绍android suspend and resume过程:
原文链接:http://blog.csdn.net/uranus_wm/article/details/11569913
uart_asm_init初始化串口,可以打印单个字符
read_om是通过om pin判断cpu是sdcard启动还是emmc启动等等
关于系统正常开机流程,以前写过一篇文章专门介绍android2.2 boot process details,比较简单,以后再补充吧!
原文链接:http://blog.csdn.net/uranus_wm/article/details/7864585