转载请标注原文地址: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