當(dāng)我們接觸電源管理的時(shí)候,最簡(jiǎn)單的流程就是?關(guān)機(jī)重啟?,但是仔細(xì)分析其涉及的所有源代碼就會(huì)發(fā)現(xiàn),關(guān)機(jī)重啟雖然簡(jiǎn)單,但是“?麻雀雖小,五臟俱全?”,涉及到的軟件模塊非常的多,涉及的流程:Linux應(yīng)用(busybox)-》Linux內(nèi)核-》BL31-》SCP-》PMIC/CRU等硬件。所以是一個(gè)入門(mén)學(xué)習(xí),特別是還沒(méi)接觸過(guò)Linux內(nèi)核代碼的好機(jī)會(huì),下面進(jìn)入代碼的海洋遨游,?超級(jí)干貨?!
1. 關(guān)機(jī)重啟軟件流程框圖
在Linux系統(tǒng)上的處理分為**用戶(hù)態(tài)**空間、**內(nèi)核**空間、 **ATF** 、**SCP**四個(gè)階段(ATF是ARM獨(dú)有的,SCP在復(fù)雜SoC上才有應(yīng)用)來(lái)處理:
1.1 用戶(hù)層
利用reboot、poweroff等命令進(jìn)行關(guān)機(jī),在應(yīng)用層會(huì)執(zhí)行:
發(fā)送SIGTERM給所有進(jìn)程,讓進(jìn)程正常退出
發(fā)送SIGKILL給所有進(jìn)程,將其殺掉,并等待一段時(shí)間
調(diào)用reboot系統(tǒng)調(diào)用讓系統(tǒng)關(guān)機(jī)/重啟
1.2?Linux內(nèi)核層
reboot系統(tǒng)調(diào)用會(huì)進(jìn)入內(nèi)核,具體流程為:
reboot系統(tǒng)調(diào)用根據(jù)參數(shù)找到kernel_power_off/reset
向關(guān)心reboot事件的進(jìn)程發(fā)送消息--blocking_notifier_call_chain
內(nèi)核Kobject狀態(tài)發(fā)生改變不通知用戶(hù)空間--usermodehelper_disable
關(guān)閉所有的設(shè)備--device_shutdown
禁止CPU熱插拔,設(shè)置當(dāng)前CPU為第一個(gè)在線(xiàn)CPU,把新任務(wù)轉(zhuǎn)移到當(dāng)前CPU上--migrate_to_reboot_cpu
關(guān)閉syscore設(shè)備--syscore_shutdown
提示用戶(hù)空間系統(tǒng)將要關(guān)閉--pr_emerg
禁止cpu硬件中斷--local_irq_disable
其他cpu處于非工作狀態(tài)--smp_send_stop
調(diào)用psci接口,執(zhí)行smc指令,關(guān)閉armcpu--pmm_power_off/ rese->psci_sys_poweroff/reset->invoke_psci_fn->
?arm_smccc_smc->SMCCC SMCCC_SMC
1.3?ATF層
執(zhí)行SMC指令后會(huì)觸發(fā)異常,進(jìn)入ATF的BL31中繼續(xù)執(zhí)行:
進(jìn)入異常向量處理的入口sync_exception_aarch64
跳轉(zhuǎn)執(zhí)行rt_svc_desc_t結(jié)構(gòu)體保存的服務(wù)std_svc_smc_handler
執(zhí)行psci相關(guān)處理。通用psci的處理函數(shù)psci_system_off和psci_system_reset,通過(guò)調(diào)用平臺(tái)提供的system_off、 system_reset接口將psci消息轉(zhuǎn)化為scmi消息發(fā)給SCP模塊,實(shí)現(xiàn)最終的關(guān)機(jī)、重啟。如果如果沒(méi)有SCP固件的系統(tǒng),會(huì)在ATF里面操作硬件寄存器進(jìn)行關(guān)機(jī)重啟處理。
1.4?SCP層
ATF通過(guò)scim消息發(fā)送給MHU硬件并產(chǎn)生中斷,SCP接受到中斷后內(nèi)部依次進(jìn)行處理的模塊為:
mhu-->transport-->scmi-->scmi_system_power-->power_domain-->ppu/system_power-->i2c/cru,最后SCP固件通過(guò)控制PMIC/CRU的硬件寄存器實(shí)現(xiàn)對(duì)系統(tǒng)的關(guān)機(jī)重啟設(shè)置。
2.?Busybox中的關(guān)機(jī)重啟命令
執(zhí)行關(guān)機(jī)重啟的系統(tǒng)命令,例如shutdown/poweroff/halt/reboot/init命令
進(jìn)程及服務(wù)如果提前會(huì)被正確的中止,我們就說(shuō)其是安全的退出。
通常關(guān)機(jī)重啟命令需要管理員權(quán)限執(zhí)行,所在系統(tǒng)目錄為/sbin/*,如下為shutdown命令:
?命令格式 ?[root@localhost ~]# shutdown [選項(xiàng)] 時(shí)間 [警告信息] ?選項(xiàng): ?-c:取消已經(jīng)執(zhí)行的 shutdown 命令; ?-h:關(guān)機(jī); ?-r:重啟; |
init命令相關(guān)執(zhí)行:
?[root@localhost~]# ?init 0 ?#關(guān)機(jī),也就是調(diào)用系統(tǒng)的 0 級(jí)別 ?[root@localhost ~】# init 6 ?#重啟,也就是調(diào)用系統(tǒng)的 6 級(jí)別 |
現(xiàn)在Linux里面這些命令基本都使用busybox實(shí)現(xiàn)的,代碼參考: https://busybox.net/downloads/
busybox啟動(dòng)的時(shí)候,會(huì)注冊(cè)reboot的處理信號(hào)
initinit.c中init_main函數(shù)在初始化的時(shí)候調(diào)用
? ?sigaddset(&G.delayed_sigset, ?SIGUSR1); /* halt */ ???sigaddset(&G.delayed_sigset, SIGTERM); /* reboot */ ???sigaddset(&G.delayed_sigset, SIGUSR2); /* poweroff */ ???? ???/* Now run the looping stuff for the rest of forever */ ???? while (1) { ??? ?????/* ?(Re)run the respawn/askfirst stuff */ ???????? run_actions(RESPAWN | ?ASKFIRST); ? ???????? /* Wait for any signal (typically it's SIGCHLD) */ ???????? check_delayed_sigs(NULL); /* NULL timespec makes it wait */ ???????? ..... ???? } |
check_delayed_sigs()函數(shù)會(huì)收到reboot的信號(hào)
運(yùn)行busybox reboot的時(shí)候,reboot—> halt_main,可知會(huì)執(zhí)行 halt_main()函數(shù),在inithalt.c中
static ?const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM ?}; ?#? define RB_HALT_SYSTEM? 0xcdef0123 ?#? define RB_ENABLE_CAD?? 0x89abcdef ?#? define RB_DISABLE_CAD? 0 ?#? define RB_POWER_OFF??? 0x4321fedc ?#? define RB_AUTOBOOT???? 0x01234567 ? ?flags = getopt32(argv, ?"d:+nfwi", &delay); ? ?if (!(flags & 4)) { /* no -f */ ???? rc = kill(pidlist[0], ?signals[which]); ?} ?else{ ???? rc = reboot(magic[which]); ?} |
這里可以看出來(lái),分為兩個(gè)流程:
當(dāng)reboot命令沒(méi)有加-f的時(shí)候,直接使用kill發(fā)送信號(hào)到busybox執(zhí)行halt_reboot_pwoff函數(shù)
直接使用-f的話(huà),直接使用reboot系統(tǒng)調(diào)用接口,通知內(nèi)核,讓內(nèi)核執(zhí)行重啟操作,簡(jiǎn)單粗暴
如果1中發(fā)送kill命令的SIGTERM 信號(hào)后,在busybox的輪詢(xún)處理函數(shù)中會(huì)接收信號(hào)進(jìn)行處理,如下:
static ?void check_delayed_sigs(struct timespec *ts) ?{ ???? int sig = ?sigtimedwait(&G.delayed_sigset, /* ?siginfo_t */ NULL, ts); ???? if (sig <= 0) ???????? return; ? ???? /* ?The signal "sig" was caught */ ?#if ENABLE_FEATURE_USE_INITTAB ???? if (sig == SIGHUP) ???????? reload_inittab(); ?#endif ???? if (sig == SIGINT) ???????? run_actions(CTRLALTDEL); ???? if (sig == SIGQUIT) { ???????? exec_restart_action(); ???????? /* returns only if no restart action defined */ ???? } ???? if ((1 << sig) & (0 ?#ifdef SIGPWR ???????? | (1 << SIGPWR) ?#endif ???????? | (1 << SIGUSR1) ???????? | (1 << SIGUSR2) ???????? | (1 << SIGTERM) ???? )) { ???????? halt_reboot_pwoff(sig); ???? } ???? /* ?if (sig == SIGCHLD) do nothing */ ?} |
在busybox內(nèi)部解析后會(huì)執(zhí)行halt_reboot_pwoff()函數(shù)
?#define ?RB_AUTOBOOT??????? 0x01234567 ?#define RB_POWER_OFF?????? 0x4321fedc ? ?static void halt_reboot_pwoff(int sig) ?{ ???????? const char *m; ???????? unsigned rb; ? ???????? ?reset_sighandlers_and_unblock_sigs(); ???????? ?run_shutdown_and_kill_processes();//進(jìn)行關(guān)機(jī)通知其他進(jìn)程處理 ? ???????? m = "halt"; ???????? rb = RB_HALT_SYSTEM; ???????? if (sig == SIGTERM) { ???????????????? m = "reboot"; ???????????????? rb = RB_AUTOBOOT; ???????? } else if (sig == SIGUSR2) { ???????????????? m = ?"poweroff"; ???????????????? rb = RB_POWER_OFF; ???????? } ???????? message(L_CONSOLE, ?"Requesting system %s", m); ???????? pause_and_low_level_reboot(rb); ?} |
halt_reboot_pwoff()分為三步:
發(fā)送SIGTERM給所有進(jìn)程,讓進(jìn)程正常退出
發(fā)送SIGKILL給所有進(jìn)程,將其殺掉
讓系統(tǒng)重啟
發(fā)送SIGTERM和SIGKILL信號(hào)給其他進(jìn)程
static ?void run_shutdown_and_kill_processes(void) ?{ ???????? run_actions(SHUTDOWN); ? ???????? message(L_CONSOLE | L_LOG, ?"The system is going down NOW!"); ? ???????? /* Send signals to every ?process _except_ pid 1 */ ???????? kill(-1, ?SIGTERM); ???????? message(L_CONSOLE, "Sent ?SIG%s to all processes", "TERM"); ???????? sync(); ???????? sleep1(); ? ???????? kill(-1, ?SIGKILL); ???????? message(L_CONSOLE, "Sent ?SIG%s to all processes", "KILL"); ???????? sync(); ???????? /*sleep1(); - callers take care ?about making a pause */ ?} |
調(diào)用子進(jìn)程進(jìn)行reboot系統(tǒng)調(diào)用:
Go ?static ?void pause_and_low_level_reboot(unsigned magic) ?{ ???????? pid_t pid; ? ???????? sleep1(); ???????? pid = vfork(); ???????? if (pid == 0) { /* child */ ???????????????? reboot(magic); ???????????????? _exit(EXIT_SUCCESS); ???????? } ? ???????? waitpid(pid, NULL, 0); ???????? sleep1(); /* paranoia */ ???????? _exit(EXIT_SUCCESS); ?} |
reboot(magic);進(jìn)行了系統(tǒng)調(diào)用。 ?
3. Linux內(nèi)核中的處理
內(nèi)核中代碼執(zhí)行流程圖:
關(guān)機(jī)主要過(guò)程總結(jié):
reboot系統(tǒng)調(diào)用根據(jù)參數(shù)找到kernel_power_off/reset
向關(guān)心reboot事件的進(jìn)程發(fā)送消息--blocking_notifier_call_chain
內(nèi)核Kobject狀態(tài)發(fā)生改變不通知用戶(hù)空間--usermodehelper_disable
關(guān)閉所有的設(shè)備--device_shutdown
禁止CPU熱插拔,設(shè)置當(dāng)前CPU為第一個(gè)在線(xiàn)CPU,把新任務(wù)轉(zhuǎn)移到當(dāng)前CPU上--migrate_to_reboot_cpu
關(guān)閉syscore設(shè)備--syscore_shutdown
提示用戶(hù)空間系統(tǒng)將要關(guān)閉--pr_emerg
禁止cpu硬件中斷--local_irq_disable
其他cpu處于非工作狀態(tài)--smp_send_stop
調(diào)用psci接口,執(zhí)行smc指令,關(guān)閉arm cpu--pmm_power_off/ rese->psci_sys_poweroff/reset->invoke_psci_fn->
?arm_smccc_smc->SMCCC SMCCC_SMC ?
3.1 系統(tǒng)調(diào)用實(shí)現(xiàn)
在kernel/reboot.c中聲明了rboot系統(tǒng)調(diào)用的實(shí)現(xiàn):
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,void __user *, arg) ?{ ???? ...... ???? switch (cmd) { ???????? case LINUX_REBOOT_CMD_POWER_OFF: ???????????????? kernel_power_off(); ???????????????? do_exit(0); ???????????????? break; |
其中cmd就是系統(tǒng)調(diào)用傳進(jìn)來(lái)的magic值,其他的值定義為:
#define??????? LINUX_REBOOT_CMD_RESTART? ?0x01234567 ?#define??????? ?LINUX_REBOOT_CMD_HALT?????0xCDEF0123 ?#define???? ???LINUX_REBOOT_CMD_CAD_ON? ? 0x89ABCDEF ?#define??????? ?LINUX_REBOOT_CMD_CAD_OFF? 0x00000000 ?#define??????? LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC ?#define??????? ?LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 ?#define???????LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2 ?#define??????? ?LINUX_REBOOT_CMD_KEXEC? ? 0x45584543 |
reboot系統(tǒng)調(diào)用實(shí)現(xiàn)過(guò)程:
1)判斷調(diào)用者的用戶(hù)權(quán)限,如果不是超級(jí)用戶(hù)(superuser),則直接返回錯(cuò)誤(這也是我們?cè)儆脩?hù)空間執(zhí)行reboot、halt、poweroff等命令時(shí),必須是root用戶(hù)的原因); 2)判斷傳入的magic number是否匹配,如果不匹配,直接返回錯(cuò)誤。這樣就可以盡可能的防止誤動(dòng)作發(fā)生; 3)調(diào)用reboot_pid_ns接口,檢查是否需要由該接口處理reboot請(qǐng)求。這是一個(gè)有關(guān)pid namespaces的新特性,也是Linux內(nèi)核重要的知識(shí)點(diǎn); 4)如果是POWER_OFF命令,且沒(méi)有注冊(cè)power off的machine處理函數(shù)(pmm_power_off),把該命令轉(zhuǎn)換為HALT命令; 5)根據(jù)具體的cmd命令,執(zhí)行具體的處理,包括, ???? 如果是RESTART或者RESTART2命令,調(diào)用kernel_restart。 ???? 如果是CAD_ON或CAD_OFF命令,更新C_A_D的值,表示是否允許通過(guò)Ctrl+Alt+Del組合鍵重啟系統(tǒng)。 ???? 如果是HALT命令,調(diào)用kernel_halt。 ???? 如果是POWER_OFF命令,調(diào)用kernel_power_off。 ???? 如果是KEXEC命令,調(diào)用kernel_kexec接口。 ???? 如果是SW_SUSPEND,調(diào)用hibernate接口; 6)返回上述的處理結(jié)果,系統(tǒng)調(diào)用結(jié)束。 |
3.2 內(nèi)核關(guān)機(jī)函數(shù)分析
這里我們以關(guān)機(jī)poweroff命令為例,進(jìn)行代碼分析,重啟流程相似。
void ?kernel_power_off(void) ?{ ?????kernel_shutdown_prepare(SYSTEM_POWER_OFF); ? ? ? ?if (pmm_power_off_prepare) ? ? ? ? ? ? ? ?pmm_power_off_prepare();//PM相關(guān)的power off prepare函數(shù) ???????? migrate_to_reboot_cpu();//將當(dāng)前的進(jìn)程(task)移到一個(gè)CPU上 ???????? syscore_shutdown(); //syscore的關(guān)閉流程,將系統(tǒng)核心器件關(guān)閉(例如中斷等) ???????? pr_emerg("Power ?down "); ???????? ?kmsg_dump(KMSG_DUMP_POWEROFF);//向這個(gè)世界發(fā)出最后的聲音(打印日志) ???????? machine_power_off();//soc基本的關(guān)閉 ?} |
1)調(diào)用kernel_xxx_prepare函數(shù),進(jìn)行restart/halt/power_off前的準(zhǔn)備工作,包括, ?????? 調(diào)用blocking_notifier_call_chain接口,向關(guān)心reboot事件的進(jìn)程,發(fā)送SYS_RESTART、 SYS_HALT或者SYS_POWER_OFF事件。對(duì)RESTART來(lái)說(shuō),還好將cmd參數(shù)一并發(fā)送出去。 ?????? 將系統(tǒng)狀態(tài)設(shè)置為相應(yīng)的狀態(tài)(SYS_RESTART、SYS_HALT或SYS_POWER_OFF)。 ?????? 調(diào)用usermodehelper_disable接口,禁止User mode helper。 ?????? 調(diào)用device_shutdown,關(guān)閉所有的設(shè)備(具體內(nèi)容會(huì)在下一節(jié)講述); 2)如果是power_off,且存在PM相關(guān)的power off prepare函數(shù)(pm_power_off_prepare),則調(diào)用該回調(diào)函數(shù); 3)調(diào)用migrate_to_reboot_cpu接口,將當(dāng)前的進(jìn)程(task)移到一個(gè)CPU上; ?注2:對(duì)于多CPU的機(jī)器,無(wú)論哪個(gè)CPU觸發(fā)了當(dāng)前的系統(tǒng)調(diào)用,代碼都可以運(yùn)行在任意的CPU上。這個(gè)接口將代碼分派到一個(gè)特定的CPU上,并禁止調(diào)度器分派代碼到其它CPU上。也就是說(shuō),這個(gè)接口被執(zhí)行后,只有一個(gè)CPU在運(yùn)行,用于完成后續(xù)的reboot動(dòng)作。 4)調(diào)用syscore_shutdown接口,將系統(tǒng)核心器件關(guān)閉(例如中斷等); 5)調(diào)用printk以及kmsg_dump,向這個(gè)世界發(fā)出最后的聲音(打印日志); 6)最后,由machine-core的代碼,接管后續(xù)的處理。 |
3.3 關(guān)閉所有設(shè)備處理
kernel_shutdown_prepare-->device_shutdown會(huì)關(guān)閉所有設(shè)備。設(shè)備關(guān)閉的過(guò)程是遍歷全局變量devices_kset里面的所有設(shè)備,并從鏈表中刪除,執(zhí)行相關(guān)的shuntdown回調(diào)函數(shù)。函數(shù)處理過(guò)程如下:
void ?device_shutdown(void) ?{ ???????? struct device *dev, *parent; ? ???????? wait_for_device_probe(); ???????? device_block_probing(); ? ???????? ?spin_lock(&devices_kset->list_lock); ???????? while ?(!list_empty(&devices_kset->list)) { ???????????????? dev = ?list_entry(devices_kset->list.prev, struct device, ???????????????????????????????? ?kobj.entry); ? ?????????parent = ?get_device(dev->parent); ?????????get_device(dev); ? ?????????list_del_init(&dev->kobj.entry); ? ? ? ? ? ?spin_unlock(&devices_kset->list_lock); ? ? ? ?/* hold lock to avoid ?race with probe/release */ ? ? ? ? ? if (parent) ? ? ? ? ? ? ? ? device_lock(parent); ??????????device_lock(dev); ? ? ? ? ? ? ?/* Don't allow any more ?runtime suspends */ ? ? ? ? ? ? pm_runtime_get_noresume(dev); ? ? ? ? ? ? pm_runtime_barrier(dev); ? ????????????if (dev->class ?&& dev->class->shutdown_pre) { ? ? ? ? ? ? if ?(initcall_debug) ? ? ? ? ? ? ? ? ? ? ?dev_info(dev, ?"shutdown_pre "); ? ? ? ? ? ? ? dev->class->shutdown_pre(dev); ????????????} ???????????????? if (dev->bus ?&& dev->bus->shutdown) { ? ? ? ? ? ? ? ? ? ?if ?(initcall_debug) ? ? ? ? ? ? ? ? ? ? ? ? ?dev_info(dev, "shutdown "); ???????????????????????? dev->bus->shutdown(dev); ???????????????? } else if ?(dev->driver && dev->driver->shutdown) { ? ? ? ? ? ? ? ? ? ? ?if ?(initcall_debug) ? ? ? ? ? ? ? ? ? ? ? ? ? dev_info(dev, "shutdown "); ???????????????????????? dev->driver->shutdown(dev); ???????????????? } ? ? ? ? ? ? ? ? device_unlock(dev); ? ? ? ? ? ? ? ? if (parent) ? ? ? ? ? ? ? ? ? ? ? device_unlock(parent); ? ? ? ? ? ? ? ? put_device(dev); ? ? ? ? ? ? ? put_device(parent); ? ??????????????spin_lock(&devices_kset->list_lock); ???????? } ???????? ?spin_unlock(&devices_kset->list_lock); ?} |
1)遍歷devices_kset的鏈表,取出所有的設(shè)備(struct device); 2)將該設(shè)備從鏈表中刪除; 3)調(diào)用pmm_runtime_get_noresume和pmm_runtime_barrier接口,停止所有的Runtime相關(guān)的電源管理動(dòng)作; 4)如果該設(shè)備的bus提供了shutdown函數(shù),優(yōu)先調(diào)用bus的shutdown,關(guān)閉設(shè)備; 5)如果bus沒(méi)有提供shutdown函數(shù),檢測(cè)設(shè)備driver是否提供,如果提供,調(diào)用設(shè)備driver的shutdown,關(guān)閉設(shè)備; 6)直至處理完畢所有的設(shè)備。 |
系統(tǒng)中所有的設(shè)備都在“/sys/devices/”目錄下,這些設(shè)備是一個(gè)鏈表結(jié)構(gòu)串起來(lái)的,devices_kset是鏈表頭,里面都是struct device,然后找到對(duì)應(yīng)的struct bus_type和struct device_driver等,然后按照優(yōu)先級(jí)例如:class>bus>driver執(zhí)行對(duì)應(yīng)的shutdown回調(diào)函數(shù)。
3.4 多CPU調(diào)度相關(guān)處理
對(duì)于多CPU的機(jī)器,無(wú)論哪個(gè)CPU觸發(fā)了當(dāng)前的系統(tǒng)調(diào)用,代碼都可以運(yùn)行在任意的CPU上。這個(gè)接口將代碼分派到一個(gè)特定的CPU上,并禁止調(diào)度器分派代碼到其它CPU上。也就是說(shuō),這個(gè)接口被執(zhí)行后,只有一個(gè)CPU在運(yùn)行,用于完成后續(xù)的reboot動(dòng)作。
void ?migrate_to_reboot_cpu(void) ?{ ???????? /* The boot cpu is always ?logical cpu 0 */ ???????? int cpu = reboot_cpu; ? ???????? cpu_hotplug_disable(); ? ???????? /* Make certain the cpu I'm ?about to reboot on is online */ ???????? if (!cpu_online(cpu)) ???????????????? cpu = ?cpumask_first(cpu_online_mask); ? ??? ?????/* Prevent races with other tasks ?migrating this task */ ???????? current->flags |= ?PF_NO_SETAFFINITY; ? ???????? /* Make certain I only run on ?the appropriate processor */ ???????? set_cpus_allowed_ptr(current, ?cpumask_of(cpu)); ?} |
1)CPU 0是默認(rèn)重啟使用的CPU 2)禁止CPU熱插拔 3)如果CPU 0不在線(xiàn),則設(shè)置當(dāng)前CPU為第一個(gè)在線(xiàn)的CPU 4)允許current進(jìn)程在重啟使用的CPU上運(yùn)行 |
3.5 內(nèi)核核心關(guān)閉
system core的shutdown和設(shè)備的shutdown類(lèi)似,也是從一個(gè)鏈表中,遍歷所有的system core,并調(diào)用它的shutdown接口。 ?
3.6 硬件平臺(tái)的關(guān)閉
void ?machine_power_off(void) ?{ ???????? local_irq_disable(); ???????? smp_send_stop(); ???????? if (pmm_power_off) ???????????????? pmm_power_off(); ?} |
1)屏蔽當(dāng)前CPU上的所有中斷,通過(guò)操作arm核心中的寄存器來(lái)屏蔽到達(dá)CPU上的中斷,此時(shí)中斷控制器中所有送往該CPU上的中斷信號(hào)都將被忽略。 2)對(duì)于多CPU的機(jī)器來(lái)說(shuō),Restart之前必須保證其它的CPU處于非活動(dòng)狀態(tài),由其中的一個(gè)主CPU負(fù)責(zé)Restart動(dòng)作。調(diào)用smp_send_stop接口,確保其它CPU處于非活動(dòng)狀態(tài); 這里會(huì)等待1秒時(shí)間來(lái)停止其他CPU。 3)調(diào)用PSCI相關(guān)接口實(shí)現(xiàn)相關(guān)關(guān)機(jī)操作 |
3.7 內(nèi)核PSCI相關(guān)操作
PSCI(Power State Coordination Interface)電源狀態(tài)協(xié)調(diào)接口,是ARM定義的電源管理接口規(guī)范。
PSCI初始化流程:
在kernel的setup_arch啟動(dòng)時(shí),掃描設(shè)備樹(shù)節(jié)點(diǎn)信息關(guān)于psci部分,根據(jù)compatible來(lái)匹配到psci_0_2_init()函數(shù),然后進(jìn)入psci_probe()函數(shù),并在psci_0_2_set_functions()函數(shù)中設(shè)置相關(guān)的函數(shù)指針:
start_kernel() -> setup_arch() ->psci_dt_init() -> psci_0_2_init() -> psci_probe() ->psci_0_2_set_functions()
設(shè)備樹(shù)里面的信息如下里標(biāo)記的版本是psci-0.2,method是使用smc。
psci { ? ? ? ?compatible = "arm,psci-0.2"; ? ? ? ? ?method = "smc"; }; |
psci_0_2_set_functions會(huì)給處理函數(shù)賦值
static ?void __init psci_0_2_set_functions(void) ?{ ???????? pr_info("Using standard ?PSCI v0.2 function IDs "); ???????? psci_ops.get_version = ?psci_get_version; ? ???????? ?psci_function_id[PSCI_FN_CPU_SUSPEND] = ???????????????????????????????????????? ?PSCI_FN_NATIVE(0_2, CPU_SUSPEND); ???????? psci_ops.cpu_suspend = ?psci_cpu_suspend; ? ???????? ?psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; ???????? psci_ops.cpu_off = ?psci_cpu_off; ? ???????? ?psci_function_id[PSCI_FN_CPU_ON] = PSCI_FN_NATIVE(0_2, CPU_ON); ???????? psci_ops.cpu_on = psci_cpu_on; ? ???????? ..... ???????? arm_pm_restart = ?psci_sys_reset; ???????? pm_power_off = psci_sys_poweroff; ?} |
PSCI關(guān)機(jī)流程:
#define ?PSCI_0_2_FN_BASE??????????????????????? ?0x84000000 ?#define PSCI_0_2_FN(n)????????????????????????? (PSCI_0_2_FN_BASE + ?(n)) ?#define PSCI_0_2_FN_SYSTEM_OFF????????????????? PSCI_0_2_FN(8) ? ?static void psci_sys_poweroff(void) ?{ ???????? ?invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); ?} |
PSCI_0_2_FN_SYSTEM_OFF的值計(jì)算為:0x84000000+8,查看ARM PSCI手冊(cè):
invoke_psci_fn()在smc模式下對(duì)應(yīng)__invoke_psci_fn_smc()函數(shù):
static ?unsigned long __invoke_psci_fn_smc(unsigned long function_id, ???????????????????????? unsigned long ?arg0, unsigned long arg1, ???????????????????????? unsigned long ?arg2) ?{ ???????? struct arm_smccc_res res; ? ???????? arm_smccc_smc(function_id, ?arg0, arg1, arg2, 0, 0, 0, 0, &res); ???????? return res.a0; ?} |
arm_smccc_smc()函數(shù)的實(shí)現(xiàn)為匯編代碼,在arch/arm/kernel/smccc-call.S中
.macro SMCCC_SMC ?? ?__SMC(0) ? ? ? .endm ???????? ?/* 定義SMCCC宏,其參數(shù)為instr */ ???????? .macro SMCCC instr ?/* 將normal world中的寄存器入棧,保存現(xiàn)場(chǎng) */ ?UNWIND(??????? .fnstart) ???????? mov??????? r12, sp? /* r12指向老的sp地址 */ ???????? push??????? {r4-r7}? /* 推r4-r7入棧,則sp = sp - 4 * 4 */ ?UNWIND(??????? .save??????? {r4-r7}) ???????? ldm??????? r12, {r4-r7}? /* 把r12指向的內(nèi)容的刷入r4-r7,其實(shí)就是把參數(shù)a4-a7存入r4-r7 ???????? instr??? /* 執(zhí)行instr參數(shù)的內(nèi)容,即執(zhí)行smc切換 */ ???????? pop??????? {r4-r7}?? /* 出棧操作,恢復(fù)現(xiàn)場(chǎng) */ ???????? ldr??????? r12, [sp, #(4 * 4)] ???????? stm??????? r12, {r0-r3} ???????? bx??????? lr ?UNWIND(??????? .fnend) ???????? .endm ???????? ?ENTRY(__arm_smccc_smc) ???????? SMCCC SMCCC_SMC ?ENDPROC(__arm_smccc_smc) ? ?#define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL) |
SMCCC宏如下,smc指令觸發(fā)一個(gè)安全監(jiān)視器異常后,將棧上的數(shù)據(jù)存到x0~x3上,回頭看__invoke_psci_fn_smc函數(shù)實(shí)際是返回x0的結(jié)果。
由于smccc_smc函數(shù)的入?yún)⒂?strong>9個(gè)參數(shù),按照約定,前4個(gè)參數(shù)存在r0 - r3,其他參數(shù)從右向左入棧。
r0=a0, r1=a1, r2=a2, r3=a3, r4=a4, r5=a5, r6=a6,r7=a7
進(jìn)入ATF中EL3模式執(zhí)行:
smc指令是arm-v8手冊(cè)中定義的一個(gè)指令,這個(gè)安全監(jiān)視器觸發(fā)一個(gè)異常,然后進(jìn)入到EL3。 EL3:安全監(jiān)控異常級(jí)別。異常級(jí)別,用于執(zhí)行安全監(jiān)視器代碼,用于處理非安全狀態(tài)和安全狀態(tài)之間的轉(zhuǎn)換。EL3始終處于Secure狀態(tài). ?
4.?ATF Bl31中的處理
4.1 ATF 軟件流程框圖
BL31中smc異常觸發(fā)流程圖
執(zhí)行SMC指令后會(huì)觸發(fā)異常,進(jìn)入ATF的BL31中繼續(xù)執(zhí)行:
在Linux側(cè)調(diào)用smc異常之后,會(huì)根據(jù)中斷向量表觸發(fā)cpu的同步異常sync_exception_aarch64/32
然后跳轉(zhuǎn)執(zhí)行到handle_sync_exception->smc_handler64/32中
根據(jù)_RT_SVC_DESCS_START_+RT_SVC_DESC_HANDLE的位置,跳轉(zhuǎn)執(zhí)行rt_svc_desc_t結(jié)構(gòu)體保存的服務(wù)std_svc_smc_handler
執(zhí)行psci相關(guān)處理,找到psci_system_off和psci_system_rese處理函數(shù)。ATF直接處理如果是關(guān)機(jī)就執(zhí)行halt指令,重啟則通過(guò)設(shè)置gpio,或者轉(zhuǎn)送給SCP處理。
最后跳轉(zhuǎn)到el3_exit返回Linux側(cè)。
SMC異常觸發(fā)執(zhí)行流程:
進(jìn)入ATF的方式觸發(fā)異常:同步異常SMC、異步異常(irq,fiq)
如果是同步異常,那么一定是在linux或tee中發(fā)生了smc調(diào)用,此時(shí)進(jìn)入跳轉(zhuǎn)ATF中異常向量表中的同步異常程序smc_handler64或smc_handler32
在該程序中,解析smc id,來(lái)選擇跳轉(zhuǎn)到具體哪一個(gè)rt-svc(runtime service)
如果是異步異常,那么一定是觸發(fā)了irq或fiq或serror中斷等,此時(shí)進(jìn)入跳轉(zhuǎn)ATF中異常向量表中的異步異常程序,進(jìn)而跳轉(zhuǎn)到響應(yīng)的中斷處理函數(shù).
4.2?內(nèi)存布局bl31_entrypoint
編譯使用的lds文件是arm-trusted-firmware/bl31/bl31.ld.S,開(kāi)頭就可以看到入口是bl31_entrypoint:
ENTRY(bl31_entrypoint) |
bl31_entrypoint在bl31/aarch64/bl31_entrypoint.S中定義
可以看到設(shè)置_exception_vectors為runtime_exceptions函數(shù)的:
/* ?--------------------------------------------------------------------- ????????? * For !RESET_TO_BL31 systems, ?only the primary CPU ever reaches ????????? ?* bl31_entrypoint() during the cold boot flow, so the cold/warm boot ????????? * and primary/secondary CPU ?logic should not be executed in this case. ????????? * ????????? * Also, assume that the ?previous bootloader has already initialised the ??????? ??* SCTLR_EL3, including the endianness, and ?has initialised the memory. ????????? * ?--------------------------------------------------------------------- ????????? */ ???????? el3_entrypoint_common??????????????????????????????????????? ???????????????? _init_sctlr=0??????????????????????????????????????? ???????????????? ?_warm_boot_mailbox=0??????????????????????????????? ???????????????? ?_secondary_cold_boot=0??????????????????????????????? ???????????????? _init_memory=0??????????????????????????????? ???????? ???????????????? _init_c_runtime=1??????????????????????????????? ???????????????? _exception_vectors=runtime_exceptions??????????????? ???????????????? ?_pie_fixup_size=BL31_LIMIT - BL31_BASE |
在bl31/aarch64/runtime_exceptions.S中
???? .globl??????? runtime_exceptions ?vector_base runtime_exceptions??????? //定義 .vectors ?vector_entry sync_exception_aarch64 ???????? handle_sync_exception ???????? check_vector_size ?sync_exception_aarch64 ?vector_entry sync_exception_aarch32 ???????? handle_sync_exception ???????? check_vector_size ?sync_exception_aarch32 |
vector_base 是一個(gè)宏,在include/arch/aarch64/asm_macros.S中定義:
???????? .macro vector_base? label, section_name=.vectors//label為標(biāo)號(hào)以冒號(hào)結(jié)尾 ???????? .section section_name, ?"ax"http://指定代碼段必須存放在.vectors段里, “ax”表示該段可執(zhí)行并且可‘a(chǎn)’讀和可‘x’執(zhí)行 ???????? .align 11, 0//地址方式對(duì)齊11 其余字節(jié)用0填充 ???????? label: ???????? .endm |
同樣其他宏經(jīng)過(guò)轉(zhuǎn)化如下:
?.section ?.vectors, "ax"??????? //指定代碼段必須存放在.vectors段里, “ax”表示該段可執(zhí)行并且可‘a(chǎn)’讀和可‘x’執(zhí)行 ?.align 11, 0??????????????????????? //地址方式對(duì)齊11 其余字節(jié)用0填充 ?runtime_exceptions: ???????? .section .vectors, ?"ax"http://指定代碼段必須存放在.vectors段里, “ax”表示該段可執(zhí)行并且可‘a(chǎn)’讀和可‘x’執(zhí)行 ???????? .align 7, 0??????????????????????????????? //地址方式對(duì)齊7 ???????? sync_exception_aarch64: ???????? ????????handle_sync_exception ???????????????? .if (. - ?serror_aarch64) > (32 * 4)??????? //這個(gè).應(yīng)該是當(dāng)前位置 - 段的開(kāi)頭地址 如果大于 32條指令 ???????????? .error "Vector exceeds ?32 instructions"??????????????? //向量超過(guò)32條指令 ???????????? .endif ???????? sync_exception_aarch32 ????? ????????handle_sync_exception ???????????????? .if (. - ?serror_aarch64) > (32 * 4)??????? //這個(gè).應(yīng)該是當(dāng)前位置 - 段的開(kāi)頭地址 如果大于 32條指令 ???????????? .error "Vector exceeds ?32 instructions"??????????????? //向量超過(guò)32條指令 ???????????? .endif |
4.3?runtime服務(wù)程序初始化
bl31_entrypoint入口向下執(zhí)行首先是bl31_setup,然后是bl31_main
?void ?bl31_setup(u_register_t arg0, u_register_t arg1, u_register_t arg2, ???????????????? u_register_t arg3) ?{ ???????? /* Perform early ?platform-specific setup */ ???????? ?bl31_early_platform_setup2(arg0, arg1, arg2, arg3); ? ???????? /* Perform late ?platform-specific setup */ ???????? bl31_plat_arch_setup(); |
bl31_main()函數(shù):
void ?bl31_main(void) ?{ ???????? NOTICE("BL31: %s ", ?version_string); ???????? NOTICE("BL31: %s ", ?build_message); ???????? bl31_platform_setup();??????? //通用和安全時(shí)鐘初始化,其他芯片相關(guān)功能初始化 ???????? bl31_lib_init();??????? //空函數(shù) ???????? INFO("BL31: Initializing ?runtime services "); ???????? runtime_svc_init();??????? //重點(diǎn) 下面展開(kāi)分析 ???????? if (bl32_init) {??????? ???????????????? INFO("BL31: ?Initializing BL32 "); ???????????????? (*bl32_init)(); ???????? } ???????? ?bl31_prepare_next_image_entry();??????? ?//加載下一階段的入口地址 ???????? console_flush();??????? //控制臺(tái)刷新 ???????? bl31_plat_runtime_setup();??????? //空函數(shù) ?} |
runtime_svc_init()函數(shù)
//注冊(cè)smc指令相關(guān)的服務(wù) ?void runtime_svc_init(void) ?{ ???????? int rc = 0; ???????? unsigned int index, start_idx, ?end_idx; ? ???????? /* Assert the number of ?descriptors detected are less than maximum indices */ ???????? //這句話(huà)表明 ?RT_SVC_DECS_NUM時(shí)當(dāng)前加載的服務(wù)數(shù)量 ???????? assert((RT_SVC_DESCS_END >= ?RT_SVC_DESCS_START) && ???????????????????????? (RT_SVC_DECS_NUM < MAX_RT_SVCS));??????? ???????????????????????????????????????? ? ? ???????? if (RT_SVC_DECS_NUM == 0)??????? //如果沒(méi)有服務(wù)要注冊(cè) ???????????????? return; ???????? memset(rt_svc_descs_indices, ?-1, sizeof(rt_svc_descs_indices));//初始化rt_svc_descs_indices ? ???????? rt_svc_descs = (rt_svc_desc_t ?*)RT_SVC_DESCS_START;//建立一個(gè)注冊(cè)表結(jié)構(gòu)體 ???????? for (index = 0; index < ?RT_SVC_DECS_NUM; index++) { ???????????????? rt_svc_desc_t *service ?= &rt_svc_descs[index]; ????????? ???????rc = validate_rt_svc_desc(service);//判斷每一個(gè)服務(wù)的各項(xiàng)參數(shù)是否正確 ???????????????? if (rc) { ???????????????????????? ?ERROR("Invalid runtime service descriptor %p ", ???????????????????????????????? (void ?*) service); ???????????????????????? panic();??????? //不正確 ???????????????? } ???????????????? if (service->init) ?{??????? //該服務(wù)是否需要初始化 ???????????????????????? rc = ?service->init();??????? //進(jìn)行初始化 ???????????????????????? if (rc) {??????? //初始化是否成功 ???????????????????????????????? ?ERROR("Error initializing runtime service %s ", ???????????????????????????????????????????????? ?service->name); ???????????????????????????????? ?continue; ???????????????????????? } ???????????????? } ???????????????? start_idx = ?get_unique_oen(rt_svc_descs[index].start_oen, ??????????????????? ?????????????service->call_type);??????? //八位的id號(hào) ???????????????? assert(start_idx < ?MAX_RT_SVCS); ???????????????? end_idx = ?get_unique_oen(rt_svc_descs[index].end_oen, ???????????????????????????????? ?service->call_type);??????? ?//八位的id號(hào) ???????????????? assert(end_idx < ?MAX_RT_SVCS); ???????????????? for (; start_idx <= ?end_idx; start_idx++) ???????????????????????? ?rt_svc_descs_indices[start_idx] = index;//證明可以根據(jù)rt_svc_descs_indices[?]的值找到其對(duì)應(yīng)的rt_svc_descs[index]中index值 ???????? } ?} |
RT_SVC_DECS_NUM表示svc數(shù)量
#define ?RT_SVC_DECS_NUM? ((RT_SVC_DESCS_END - RT_SVC_DESCS_START) ????/ sizeof(rt_svc_desc_t)) |
RT_SVC_DESCS_START開(kāi)始的位置已經(jīng)存入了結(jié)構(gòu)體數(shù)據(jù)數(shù)組,因?yàn)樵趌d文件中進(jìn)行了內(nèi)存布局說(shuō)明
在bl31/bl31.ld.S中:
???????? RODATA_COMMON ? ???????? /* Place pubsub sections for ?events */ ???????? . = ALIGN(8); ?#include ? ???????? . = ALIGN(PAGE_SIZE); ???????? __RODATA_END__ = .; ???? } >RAM ?#else ???? ro . : { ???????? __RO_START__ = .; ???????? *bl31_entrypoint.o(.text*) ????? ???*(SORT_BY_ALIGNMENT(.text*)) ???????? *(SORT_BY_ALIGNMENT(.rodata*)) ? ???????? RODATA_COMMON |
在include/common/bl_common.ld.h中
#define ?RODATA_COMMON??????????????????????????????????????? ???????? RT_SVC_DESCS??????????????????????????????????????? ???????? ?FCONF_POPULATOR??????????????????????????????????????? ???????? PMF_SVC_DESCS??????????????????????????????????????? ???????? PARSER_LIB_DESCS??????????????????????????????? ???????? CPU_OPS??????????????????????????????????????????????? ? ?????? ??GOT??????????????????????????????????????????????? ? ???????? BASE_XLAT_TABLE_RO??????????????????????????????? ???????? EL3_LP_DESCS ? ?#define RT_SVC_DESCS??????????????????????????????????????? ???????? . = ALIGN(STRUCT_ALIGN);??????????????????????? ???????? __RT_SVC_DESCS_START__ = ?.;??????????????????????? ???????? KEEP(*(rt_svc_descs))??????????????????????????????? ???????? __RT_SVC_DESCS_END__ = .; |
rt_svc_descs段存放的內(nèi)容是通過(guò)DECLARE_RT_SVC宏來(lái)定義的:
//其中__setion("rt_svc_descs")的意思就是注冊(cè)到rt_svc_descs段中
?#define ?DECLARE_RT_SVC(_name, _start, _end, _type, ?_setup, _smch)??????? ???????? static const rt_svc_desc_t ?__svc_desc_ ## _name??????????????????????? ? ???????????????? __section("rt_svc_descs") __used = {??????????????????????? ???????????????????????? .start_oen = ?(_start),??????????????????????????????? ? ???????????????????????? .end_oen = ?(_end),??????????????????????????????? ? ???????????????????????? .call_type = ?(_type),??????????????????????????????? ? ????????????????????? ???.name = #_name,??????????????????????????????????????? ???????????????????????? .init = ?(_setup),??????????????????????????????? ? ???????????????????????? .handle = ?(_smch)??????????????????????????????? ? ???????????????? } |
例如在services/std_svc/std_svc_setup.c中
/* ?Register Standard Service Calls as runtime service */ ?DECLARE_RT_SVC( ???????????????? std_svc, ? ???????????????? OEN_STD_START, ???????????????? OEN_STD_END, ???????????????? SMC_TYPE_FAST, ???????????????? std_svc_setup, ???????????????? std_svc_smc_handler ?); ?#define OEN_STD_START??????????????????????? ?U(4)??????? /* Standard Service ?Calls */ ?#define OEN_STD_END??????????????????????? ?U(4) ?#define SMC_TYPE_FAST??????????????????????? ?UL(1) ?#define SMC_TYPE_YIELD???? ???????????????????UL(0) |
static const rt_svc_desc_t __svc_desc_std_svc服務(wù)。其服務(wù)id為SMC_TYPE_FAST<< 6 + OEN_STD_START,結(jié)束服務(wù)的id為SMC_TYPE_FAST << 6 + OEN_STD_END
service->init()會(huì)執(zhí)行std_svc_setup()函數(shù)
->psci_setup((const psci_lib_args_t *)svc_arg)
(void) plat_setup_psci_ops((uintptr_t)lib_args->mailbox_ep,
&psci_plat_pm_ops);
plat_setup_psci_ops()的定義根據(jù)平臺(tái),我們使用的是qemu,對(duì)應(yīng)plat/qemu/qemu_sbsa/sbsa_pm.c文件中:
*psci_ops = &plat_qemu_psci_pm_ops;
static ?const plat_psci_ops_t plat_qemu_psci_pm_ops = { ???????? .cpu_standby = ?qemu_cpu_standby, ???????? .pwr_domain_on = ?qemu_pwr_domain_on, ???????? .pwr_domain_off = ?qemu_pwr_domain_off, ???????? .pwr_domain_pwr_down_wfi = ?qemu_pwr_domain_pwr_down_wfi, ???????? .pwr_domain_suspend = qemu_pwr_domain_suspend, ???????? .pwr_domain_on_finish = ?qemu_pwr_domain_on_finish, ???????? .pwr_domain_suspend_finish = ?qemu_pwr_domain_suspend_finish, ???????? .system_off = qemu_system_off, ???????? .system_reset = ?qemu_system_reset, ???????? .validate_power_state = ?qemu_validate_power_state ?}; |
? 4.4 SMC異常處理入口分析
SMC命令執(zhí)行后,CPU會(huì)根據(jù)異常向量表找到sync_exception_aarch64的入口
會(huì)執(zhí)行handle_sync_exception,在bl31/aarch64/runtime_exceptions.S中
/* ?--------------------------------------------------------------------- ????????? * This macro handles ?Synchronous exceptions. ????????? * Only SMC exceptions are ?supported. ????????? * ?--------------------------------------------------------------------- ????????? */ ???????? .macro??????? handle_sync_exception ?#if ENABLE_RUNTIME_INSTRUMENTATION ???????? /* ????????? * Read the timestamp value and ?store it in per-cpu data. The value ????????? * will be extracted from ?per-cpu data by the C level SMC handler and ????????? * saved to the PMF timestamp ?region. ????????? *///存放時(shí)間戳 ???????? mrs??????? x30, cntpct_el0 ???????? str??????? x29, [sp, #CTX_GPREGS_OFFSET + ?CTX_GPREG_X29] ???????? mrs??????? x29, tpidr_el3 ???????? str??????? x30, [x29, #CPU_DATA_PMF_TS0_OFFSET] ???????? ldr??????? x29, [sp, #CTX_GPREGS_OFFSET + ?CTX_GPREG_X29] ?#endif ? ???????? mrs??????? x30, esr_el3 ?//將esr_el3存入x30 ???????? //#define ESR_EC_SHIFT U(26) ?#define ESR_EC_LENGTH U(6) ???????? //相當(dāng)于 保留 x30的bit[31-26]并將這幾位提到bit[6-0] ???????? ubfx????? ??x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH ? ???????? /* Handle SMC exceptions ?separately from other synchronous exceptions */ ???????? cmp??????? x30, #EC_AARCH32_SMC ???????? b.eq??????? smc_handler32 ? ???????? cmp??????? x30, #EC_AARCH64_SMC ???????? b.eq???? ???sync_handler64 ? ???????? cmp??????? x30, #EC_AARCH64_SYS ???????? b.eq??????? sync_handler64 ? ???????? /* Synchronous exceptions other ?than the above are assumed to be EA */ ???????? ldr??????? x30, [sp, #CTX_GPREGS_OFFSET + ?CTX_GPREG_LR] ???????? b??????? enter_lower_el_sync_ea ???????? .endm |
三種跳轉(zhuǎn)選項(xiàng)其中smc_handler32/64能夠正確觸發(fā)異常,report_unhandled_exception則是錯(cuò)誤的流程
#define ?EC_AARCH32_SMC??????????????????????? ?U(0x13) ?#define EC_AARCH64_SVC??????????????????????? ?U(0x15) ?#define EC_AARCH64_HVC?????????????????? ??????U(0x16) ?#define EC_AARCH64_SMC??????????????????????? U(0x17) |
x30里面存儲(chǔ)的是esr_el3 的26-32位,里面是什么判斷了smc64
當(dāng)前平臺(tái)架構(gòu)是aarch64的,看一下sync_handler64這個(gè)處理,在bl31/aarch64/runtime_exceptions.S中
? ?/* Load descriptor index from array ?of indices */ ???????? //在runtime_svc_init()中會(huì)將所有的section rt_svc_descs段放入rt_svc_descs_indices數(shù)組, ???????? //這里獲取該數(shù)組地址 ???????? adrp??????? x14, rt_svc_descs_indices ???????? add??????? x14, x14, rt_svc_descs_indices ???????? ldrb??????? w15, [x14, x16]//找到rt_svc在rt_svc_descs_indices數(shù)組中的index ???????? ????????? /* ????????? * Get the descriptor using the ?index ????????? * x11 = (base + off), w15 = ?index 這個(gè)index就是rt_svc_descs結(jié)構(gòu)體數(shù)組下標(biāo) ????????? * ????????? * handler = (base + off) + ?(index << log2(size)) ????????? */ ???????? adr??????? x11, (__RT_SVC_DESCS_START__ ?+ RT_SVC_DESC_HANDLE) //base + off ???????? lsl??????? w10, w15, #RT_SVC_SIZE_LOG2 //(index << log2(size)) ???????? ldr??????? x15, [x11, w10, uxtw]? //handler ?= (base + off) + (index << log2(size)) ???????? ???????? blr??????? x15//跳轉(zhuǎn)到handler ? ???????? b??????? el3_exit |
sync_handler64里找rt_svc_desc_t結(jié)構(gòu)體類(lèi)型里面的handle處理函數(shù),而這些處理函數(shù)在rt_svc_descs節(jié)中
一個(gè)問(wèn)題:怎么找到index?
例如發(fā)的一個(gè)smc消息id是0x84000000+8
?bit31決定是fast call,還是std call(yield對(duì)應(yīng)的就是std call)
?bit30表示是以32位傳參,還是以64位傳參,注意我們看了optee在linux的driver,都是以32位方式
?bit29:24 決定服務(wù)的類(lèi)型
?bit23:16reserved
?bit15:0每種call類(lèi)型下,表示range
這個(gè)地方值為4,
/* ?Register Standard Service Calls as runtime service */ ?DECLARE_RT_SVC( ???????????????? std_svc, ???????????????? OEN_STD_START, ???????????????? OEN_STD_END, ???????????????? SMC_TYPE_FAST, ???????????????? std_svc_setup, ???????????????? std_svc_smc_handler ?); ?#define OEN_STD_START????????????????????? ?U(4)??????? /* Standard Service Calls */ ?#define OEN_STD_END?????????????????????? ??U(4) ? ? |
系統(tǒng)啟動(dòng)的時(shí)候會(huì)把index信息存入到rt_svc_descs_indices里面,根據(jù)4取出來(lái)就可以了。
? ? start_idx ?= (uint8_t)get_unique_oen(service->start_oen,service->call_type); ? ? end_idx = ?(uint8_t)get_unique_oen(service->end_oen,service->call_type); ????assert(start_idx <= ?end_idx); ? ? assert(end_idx < ?MAX_RT_SVCS); ? ? ? for (; start_idx <= ?end_idx; start_idx++) ????????rt_svc_descs_indices[start_idx] ?= index; |
base+index << log2(size)找到結(jié)構(gòu)體數(shù)組index對(duì)應(yīng)的元素,然后off就是結(jié)構(gòu)體內(nèi)handle對(duì)應(yīng)的函數(shù)。
handler = (base + off) + (index <
w15 = (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE) + w15<< log2(size)
? 4.5?smc服務(wù)處理分析
std_svc_smc_handler()中,可以看到會(huì)調(diào)用psci_smc_handler函數(shù)。
/* ??* Top-level Standard Service SMC ?handler. This handler will in turn dispatch ??* calls to PSCI SMC handler ??*/ ?static uintptr_t std_svc_smc_handler(uint32_t smc_fid, ????????????????????????????? ?u_register_t x1, ????????????????????????????? ?u_register_t x2, ????????????????????????????? ?u_register_t x3, ????????????????????????????? ?u_register_t x4, ????????????????????????????? void ?*cookie, ????????????????????????????? void ?*handle, ????????????????????????????? ?u_register_t flags) ?{ ???????????????? ret = ?psci_smc_handler(smc_fid, x1, x2, x3, x4, ???????????????????? cookie, handle, ?flags); ? ? |
而在psci_smc_handler函數(shù)中,就可以看到上面?zhèn)魅氲膒sci傳入的PSCI_SYSTEM_OFF指令
case PSCI_SYSTEM_OFF: ??????psci_system_off(); ? ? ? /* We should ?never return from psci_system_off() */ ? ? ? ? ?break; |
在psci_system_off進(jìn)去就打印psci相關(guān)的打印,然后調(diào)用system_off回調(diào)。
void ?__dead2 psci_system_off(void) ?{ ???????? psci_print_power_domain_map();//打印 ? ???????? ?assert(psci_plat_pm_ops->system_off != NULL); ? ???????? /* Notify the Secure Payload ?Dispatcher */ ???????? if ((psci_spd_pm != NULL) ?&& (psci_spd_pm->svc_system_off != NULL)) { ???????????????? ?psci_spd_pm->svc_system_off(); ???????? } ? ???????? console_flush(); ? ???????? /* Call the platform specific ?hook */ ???????? ?psci_plat_pm_ops->system_off(); ? ???????? /* This function does not ?return. We should never get here */ ?} |
psci_print_power_domain_map()的打印再和設(shè)備重啟時(shí)的日志進(jìn)行對(duì)比,發(fā)現(xiàn)是一致的。
4.5?硬件平臺(tái)相關(guān)處理
在qemu平臺(tái)上的實(shí)現(xiàn)如下:
psci_plat_pm_ops系統(tǒng)初始化的時(shí)候會(huì)賦值.system_off = qemu_system_off,
static ?void __dead2 qemu_system_off(void) ?{ ?#ifdef SECURE_GPIO_BASE ???????? ERROR("QEMU System Power ?off: with GPIO. "); ???????? ?gpio_set_direction(SECURE_GPIO_POWEROFF, GPIO_DIR_OUT); ???????? ?gpio_set_value(SECURE_GPIO_POWEROFF, GPIO_LEVEL_LOW); ???????? ?gpio_set_value(SECURE_GPIO_POWEROFF, GPIO_LEVEL_HIGH); ?#else ??????? ?semihosting_exit(ADP_STOPPED_APPLICATION_EXIT, ?0); ???????? ERROR("QEMU System Off: ?semihosting call unexpectedly returned. "); ?#endif ???????? panic(); ?} |
semihosting_exit:
func ?semihosting_call ???????? hlt??????? #0xf000 ???????? ret ?endfunc semihosting_call |
對(duì)應(yīng)重啟,qemu_system_reset()函數(shù)設(shè)置GPIO實(shí)現(xiàn)
gpio_set_direction(SECURE_GPIO_RESET, ?GPIO_DIR_OUT); ???????? ?gpio_set_value(SECURE_GPIO_RESET, GPIO_LEVEL_LOW); ???????? ?gpio_set_value(SECURE_GPIO_RESET, GPIO_LEVEL_HIGH); |
如果不是ATF里面自己處理,有SCP,見(jiàn)下章節(jié)分析。 ?
5. SCP中的處理
mhu模塊: mhu_isr收到中斷 ? status = smt_channel->api->signal_message(smt_channel->id);
signal_message是smt模塊里面提供的,對(duì)共享內(nèi)存的數(shù)據(jù)進(jìn)行處理
status = ?fwk_module_bind(smt_channel->id, ???FWK_ID_API(FWK_MODULE_IDX_SMT, ?MOD_SMT_API_IDX_DRIVER_INPUT),?&smt_channel->api); |
??
審核編輯:劉清
評(píng)論