android 5.0关机流程-kernel层
2015-10-14 19:19
519 查看
前面已经讲到了lowLevelShutdown()与lowLevelReboot()都在PowerManagerService.java实现,都只是设置一个属性:SystemProperties.set(sys.powerctl, xxx);这里从这继续往下讲:
sys.powerctl属性触发开关在init.rc定义:
on property:sys.powerctl=*表示当属性sys.powerctl设置为任何值是都会跑到这里,触发动作是powerctl ${sys.powerctl},这个动作的意思是调用powerctl指令,并把sys.powerctl的值传给它。powerctl指令在init进程会执行。
从下面的表可知,powerctl对应的操作是do_powerctl
[system/core/init/keywords.h]
do_powerctl的实现代码如下:
可以看到最后其调用的是android_reboot()函数,其实现如下:
KK不同,这边直接用syscall功能,KK则通过汇编。先看看reboot()的实现代码:
[bionic/libc/bionic/reboot.cpp]
调用了__reboot,它在汇编实现 如下:
[bionic/libc/arch-arm/syscalls/__reboot.S]
__NR_reboot对应的内核入口在哪里?
[bionic/libc/kernel/uapi/asm-generic/unistd.h]
它在内核入口如下:
bionic/libc/kernel/uapi/asm-generic/unistd.h与kernel/include/uapi/asm-generic/unistd.h是对应的,方便以后代码追踪,
来看看kernel下的[kernel/include/uapi/asm-generic/unistd.h]
可以看到__NR_reboot 映射到 sys_reboot,他的声明在kernel/include/linux/syscall.h中
sys_reboot而最后的定义是在kernel/kernel/sys.c中
那么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里有下面一组宏:
这里每个SYSCALL_DEFINE后面的数字说明了,名为name的系统调用接收几个输入参数。这些宏都由一个基础宏SYSCALL_DEFINEx来实现:
到此sys_reboot系统调用在kernel中从SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg)开始往下执行:
这个函数有很多分支,我们只关心kernel_power_off()和kernel_restart()两函数就行:
都执行XX_prepare()函数:
除了前面不同,都调用了device_shutdown()函数,关闭外设。
machine_power_off() machine_resestart()函数实现:
其实现如下:
至此,kernel的关机流程运行完毕!
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_KEXEC case LINUX_REBOOT_CMD_KEXEC: ret = kernel_kexec(); break; #endif #ifdef CONFIG_HIBERNATION case LINUX_REBOOT_CMD_SW_SUSPEND: ret = hibernate(); break; #endif default: 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(); //关闭syscore printk(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();//关闭syscore if (!cmd) printk(KERN_EMERG "Restarting system.\n");//关键打印 else printk(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_TEST test_memory(); #endif #ifndef CONFIG_SPRD_PM_DEBUG pm_debug_init(); #endif /* enable arm clock auto gating*/ //sci_glb_set(REG_AHB_AHB_CTL1, BIT_ARM_AUTO_GATE_EN); #ifdef CONFIG_CPU_IDLE sc_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_POWER sprd_extic_otg_power(0); #endif sci_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的关机流程运行完毕!
相关文章推荐
- Android采用canvas绘制各种图形
- AndroidStudio快捷键
- Android判断软键盘弹出并隐藏的简单完美解决方案
- Android中Listview的getChildAt()只能更新当前显示在屏幕上的Item的解决办法
- Android数据库之SQLite数据库
- 安卓概述及开发环境
- android中EditText有光标不弹出软键盘处理(转)
- Android 抓包,监控流量工具之 mitmproxy
- Android中的Dialog和Popupwindow的区别
- 安卓百度地图开发so文件引用失败问题研究
- MulticastSocket的简单使用
- Android 属性动画(Property Animation) 完全解析 (下)
- Android Studio下添加引用jar文件和so文件
- android 他们定义对话框
- 安卓注解那些事儿
- git使用之七——Android Studio下git的正确使用
- Android位置框架之GPS精度顺藤摸瓜
- Android View绘制流程
- Android Canvas不能换行,或者不识别\n,\r\n的解决方案
- Android ExpandableListView的特殊使用——始终展开不收缩