android 5.0关机流程-kernel层

来源:互联网 发布:网络文明 编辑:程序博客网 时间:2024/06/01 09:18

前面已经讲到了lowLevelShutdown()与lowLevelReboot()都在PowerManagerService.java实现,都只是设置一个属性:SystemProperties.set(sys.powerctl, xxx);这里从这继续往下讲:

sys.powerctl属性触发开关在init.rc定义:

on property:sys.powerctl=*                                                            powerctl ${sys.powerctl}

on property:sys.powerctl=*表示当属性sys.powerctl设置为任何值是都会跑到这里,触发动作是powerctl ${sys.powerctl},这个动作的意思是调用powerctl指令,并把sys.powerctl的值传给它。powerctl指令在init进程会执行。

从下面的表可知,powerctl对应的操作是do_powerctl

[system/core/init/keywords.h]

KEYWORD(powerctl,    COMMAND, 1, do_powerctl)

do_powerctl的实现代码如下:

 794 int do_powerctl(int nargs, char **args) 795 { 796     char command[PROP_VALUE_MAX]; 797     int res; 798     int len = 0; 799     int cmd = 0; 800     char *reboot_target; 801  802     res = expand_props(command, args[1], sizeof(command)); 803     if (res) { 804         ERROR("powerctl: cannot expand '%s'\n", args[1]); 805         return -EINVAL; 806     } 807  808     if (strncmp(command, "shutdown", 8) == 0) { 809         cmd = ANDROID_RB_POWEROFF; 810         len = 8; 811     } else if (strncmp(command, "reboot", 6) == 0) { 812         cmd = ANDROID_RB_RESTART2; 813         len = 6; 814     } else { 815         ERROR("powerctl: unrecognized command '%s'\n", command); 816         return -EINVAL; 817     } 818  819     if (command[len] == ',') { 820         reboot_target = &command[len + 1]; 821     } else if (command[len] == '\0') { 822         reboot_target = ""; 823     } else { 824         ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]); 825         return -EINVAL; 826     } 827  828     return android_reboot(cmd, 0, reboot_target); 829 }

可以看到最后其调用的是android_reboot()函数,其实现如下:

107 int android_reboot(int cmd, int flags UNUSED, char *arg)108 {109     int ret;110 111     sync();112     remount_ro();113 114     switch (cmd) {115         case ANDROID_RB_RESTART:116             ret = reboot(RB_AUTOBOOT);117             break;118 119         case ANDROID_RB_POWEROFF:120             ret = reboot(RB_POWER_OFF);121             break;122 123         case ANDROID_RB_RESTART2:124             ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,125                            LINUX_REBOOT_CMD_RESTART2, arg);126             break;127 128         default:129             ret = -1;130     }131 132     return ret;133 }
可以看到如果是系统关机会走ANDROID_RB_POWEROFF分支,而重启则会走ANDROID_RB_RESTART2分支,sync() 回写block设备的内容,这是阻塞型操作;remount_ro() 把block设备remount成ro,这里有个关键LOG:SysRq : Emergency Remount R/O,这是在logkit所能看到的最后一句LOG,因为remount成ro了,后面的LOG要通过last kmsg技术导出来。syscall(__NR_reboot....直接调用了linux的__NR_reboot系统调用,这点与android KK不同,这边直接用syscall功能,KK则通过汇编。先看看reboot()的实现代码:

[bionic/libc/bionic/reboot.cpp]

 32 extern "C" int __reboot(int, int, int, void*); 33  34 int reboot(int mode) { 35   return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, mode, NULL); 36 }

调用了__reboot,它在汇编实现 如下:
[bionic/libc/arch-arm/syscalls/__reboot.S]

  3 #include <private/bionic_asm.h>  4   5 ENTRY(__reboot)  6     mov     ip, r7  7     ldr     r7, =__NR_reboot  //也用到了__NR_reboot系统调用  8     swi     #0  9     mov     r7, ip 10     cmn     r0, #(MAX_ERRNO + 1) 11     bxls    lr 12     neg     r0, r0 13     b       __set_errno_internal 14 END(__reboot)

__NR_reboot对应的内核入口在哪里?

[bionic/libc/kernel/uapi/asm-generic/unistd.h]

221 #define __NR_reboot 142 

它在内核入口如下:
bionic/libc/kernel/uapi/asm-generic/unistd.h与kernel/include/uapi/asm-generic/unistd.h是对应的,方便以后代码追踪,

来看看kernel下的[kernel/include/uapi/asm-generic/unistd.h]

422 #define __NR_reboot 142                                                                                                                                                                                  423 __SYSCALL(__NR_reboot, sys_reboot)

可以看到__NR_reboot 映射到 sys_reboot,他的声明在kernel/include/linux/syscall.h中

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

sys_reboot而最后的定义是在kernel/kernel/sys.c中

SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,void __user *, arg){struct pid_namespace *pid_ns = task_active_pid_ns(current);char buffer[256];int ret = 0;printk(KERN_EMERG "[shutdown_debug] enter reboot syscall.\n");/* We only trust the superuser with rebooting the system. */if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) {printk(KERN_EMERG "[shutdown_debug] ns_capable return,pid_ns->user_ns=0x%x.\n",pid_ns->user_ns);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)) {printk(KERN_EMERG "[shutdown_debug] magic return.magic1=%d,magic2=%d\n",magic1,magic2);return -EINVAL;}/* * If pid namespaces are enabled and the current task is in a child * pid_namespace, the command is handled by reboot_pid_ns() which will * call do_exit(). */ret = reboot_pid_ns(pid_ns, cmd);if (ret) {printk(KERN_EMERG "[shutdown_debug] reboot_pid_ns return,ret=%d.\n",ret);return ret;}/* 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_CAD_ON:C_A_D = 1;break;case LINUX_REBOOT_CMD_CAD_OFF:C_A_D = 0;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;case LINUX_REBOOT_CMD_RESTART2:if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {ret = -EFAULT;break;}buffer[sizeof(buffer) - 1] = '\0';kernel_restart(buffer);break;#ifdef CONFIG_KEXECcase LINUX_REBOOT_CMD_KEXEC:ret = kernel_kexec();break;#endif#ifdef CONFIG_HIBERNATIONcase LINUX_REBOOT_CMD_SW_SUSPEND:ret = hibernate();break;#endifdefault:ret = -EINVAL;break;}mutex_unlock(&reboot_mutex);printk(KERN_EMERG "[shutdown_debug] exit reboot syscall.\n");return ret;}

那么SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)是如何转换成asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user *arg);形式的呢?其实就是宏的一些基本用法而已,我们来简单分析一下。在include/linux/syscall.h里有下面一组宏:

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

这里每个SYSCALL_DEFINE后面的数字说明了,名为name的系统调用接收几个输入参数。这些宏都由一个基础宏SYSCALL_DEFINEx来实现:

#define SYSCALL_DEFINEx(x, sname, ...)\SYSCALL_METADATA(sname, x, __VA_ARGS__)\__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

#define __SYSCALL_DEFINEx(x, name, ...)\asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));\asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))\{\long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));\__MAP(x,__SC_TEST,__VA_ARGS__);\__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));\return ret;\}\SYSCALL_ALIAS(sys##name, SyS##name);\static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

到此sys_reboot系统调用在kernel中从SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)开始往下执行:

这个函数有很多分支,我们只关心kernel_power_off()和kernel_restart()两函数就行:

void kernel_power_off(void){printk(KERN_EMERG "[shutdown_debug] enter kernel_power_off.\n");kernel_shutdown_prepare(SYSTEM_POWER_OFF); //关闭外设if (pm_power_off_prepare)pm_power_off_prepare();migrate_to_reboot_cpu();syscore_shutdown(); //关闭syscoreprintk(KERN_EMERG "Power down.\n");//关键打印kmsg_dump(KMSG_DUMP_POWEROFF); machine_power_off();printk(KERN_EMERG "[shutdown_debug] exit kernel_power_off.\n");}

void kernel_restart(char *cmd){kernel_restart_prepare(cmd);//关闭外设migrate_to_reboot_cpu();syscore_shutdown();//关闭syscoreif (!cmd)printk(KERN_EMERG "Restarting system.\n");//关键打印elseprintk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);kmsg_dump(KMSG_DUMP_RESTART);preempt_disable();machine_restart(cmd);}

都执行XX_prepare()函数:

static void kernel_shutdown_prepare(enum system_states state){printk(KERN_EMERG "[shutdown_debug] enter kernel_shutdown_prepare.\n");blocking_notifier_call_chain(&reboot_notifier_list,(state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL);system_state = state;usermodehelper_disable();device_shutdown();printk(KERN_EMERG "[shutdown_debug] exit kernel_shutdown_prepare.\n");}void kernel_restart_prepare(char *cmd){blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);system_state = SYSTEM_RESTART;usermodehelper_disable();device_shutdown();}

除了前面不同,都调用了device_shutdown()函数,关闭外设。

machine_power_off() machine_resestart()函数实现:

void machine_power_off(void){smp_send_stop();if (pm_power_off)pm_power_off();//关机}void machine_restart(char *cmd){smp_send_stop();/* Flush the console to make sure all the relevant messages make it * out to the console drivers */arm_machine_flush_console();arm_pm_restart(reboot_mode, cmd);//重启/* Give a grace period for failure to restart of 1s */mdelay(1000);/* Whoops - the platform was unable to reboot. Tell the user! */printk("Reboot failed -- System halted\n");local_irq_disable();while (1);}
pm_power_offf() arm_pm_restart()都是一个函数指针,他们的复制,根据厂家的不同而不同,SPRD,mediatek,msm三家各有各的操作,这里是SPRD,其赋值位于kernel/drivers/platform/sprd/Pm-scx35.c:
void __init sc_pm_init(void){init_reset_vector();pm_power_off   = sc8830_power_off;arm_pm_restart = sc8830_machine_restart;pr_info("power off %pf, restart %pf\n", pm_power_off, arm_pm_restart);init_gr();setup_autopd_mode();pm_ana_ldo_config();init_led();/* disable all sleep mode */sci_glb_clr(REG_AP_AHB_MCU_PAUSE, BIT_MCU_DEEP_SLEEP_EN | BIT_MCU_LIGHT_SLEEP_EN | \BIT_MCU_SYS_SLEEP_EN | BIT_MCU_CORE_SLEEP);set_reset_vector();#ifdef CONFIG_DDR_VALIDITY_TESTtest_memory();#endif#ifndef CONFIG_SPRD_PM_DEBUGpm_debug_init();#endif/* enable arm clock auto gating*///sci_glb_set(REG_AHB_AHB_CTL1, BIT_ARM_AUTO_GATE_EN);#ifdef CONFIG_CPU_IDLEsc_cpuidle_init();#endif/*wake_lock_init(&pm_debug_lock, WAKE_LOCK_SUSPEND, "pm_not_ready");wake_lock(&pm_debug_lock);*/#if defined(CONFIG_SPRD_DEBUG)sprd_debug_init();#endif}

其实现如下:

static void sc8830_power_off(void){u32 reg_val;/*turn off all modules's ldo*/#ifdef CONFIG_SPRD_EXT_IC_POWERsprd_extic_otg_power(0);#endifsci_adi_raw_write(ANA_REG_GLB_LDO_PD_CTRL, 0x1fff);#if defined(CONFIG_ARCH_SCX15) || defined(CONFIG_ADIE_SC2723) || defined(CONFIG_ADIE_SC2723S)sci_adi_raw_write(ANA_REG_GLB_PWR_WR_PROT_VALUE,BITS_PWR_WR_PROT_VALUE(0x6e7f));do{reg_val = (sci_adi_read(ANA_REG_GLB_PWR_WR_PROT_VALUE) & BIT_PWR_WR_PROT);}while(reg_val == 0);sci_adi_raw_write(ANA_REG_GLB_LDO_PD_CTRL,0xfff);sci_adi_raw_write(ANA_REG_GLB_LDO_DCDC_PD,0x7fff);#endif#if defined(CONFIG_ADIE_SC2713S) || defined(CONFIG_ADIE_SC2713)/*turn off system core's ldo*/sci_adi_raw_write(ANA_REG_GLB_LDO_DCDC_PD_RTCCLR, 0x0);sci_adi_raw_write(ANA_REG_GLB_LDO_DCDC_PD_RTCSET, 0X7fff);#endif}static void sc8830_machine_restart(char mode, const char *cmd){local_irq_disable();local_fiq_disable();arch_reset(mode, cmd);mdelay(1000);printk("reboot failed!\n");while (1);}

至此,kernel的关机流程运行完毕!





0 0