本篇有相當(dāng)?shù)碾y度,涉及用戶棧和內(nèi)核棧的兩輪切換,CPU四次換棧,寄存器改值,將圍繞下圖來說明.
解讀
為本篇理解方便,把圖做簡(jiǎn)化標(biāo)簽說明:
user:用戶空間
kernel:內(nèi)核空間
source(...):源函數(shù)
sighandle(...):信號(hào)處理函數(shù),
syscall(...):系統(tǒng)調(diào)用,參數(shù)為系統(tǒng)調(diào)用號(hào),如sigreturn,N(表任意)
user.source():表示在用戶空間運(yùn)行的源函數(shù)
系列篇已多次說過,用戶態(tài)的任務(wù)有兩個(gè)運(yùn)行棧,一個(gè)是用戶棧,一個(gè)是內(nèi)核棧.棧空間分別來自用戶空間和內(nèi)核空間.兩種空間是有嚴(yán)格的地址劃分的,通過虛擬地址的大小就能判斷出是用戶空間還是內(nèi)核空間.系統(tǒng)調(diào)用本質(zhì)上是軟中斷,它使CPU執(zhí)行指令的場(chǎng)地由用戶棧變成內(nèi)核棧.怎么變的并不復(fù)雜,就是改變(sp和cpsr寄存器的值).sp指向哪個(gè)棧就代表在哪個(gè)棧運(yùn)行, 當(dāng)cpu在用戶棧運(yùn)行時(shí)是不能訪問內(nèi)核空間的,但內(nèi)核態(tài)任務(wù)可以訪問整個(gè)空間,而且內(nèi)核態(tài)任務(wù)沒有用戶棧.
理解了上面的說明,再來說下正常系統(tǒng)調(diào)用流程是這樣的: user.source() -> kernel.syscall(N) - > user.source() ,想要回到user.source()繼續(xù)運(yùn)行,就必須保存用戶?,F(xiàn)場(chǎng)各寄存器的值.這些值保存在內(nèi)核棧中,恢復(fù)也是從內(nèi)核棧恢復(fù).
信號(hào)消費(fèi)的過程的上圖可簡(jiǎn)化表示為: user.source() -> kernel.syscall(N) ->user.sighandle() ->kernel.syscall(sigreturn) -> user.source() 在原本要回到user.source()的中間插入了信號(hào)處理函數(shù)的調(diào)用. 這正是本篇要通過代碼來說清楚的核心問題.
順著這個(gè)思路可以推到以下幾點(diǎn),實(shí)際也是這么做的:
kernel.syscall(N) 中必須要再次保存user.source()的上下文sig_switch_context,為何已經(jīng)保存了一次還要再保存一次?
因?yàn)榈谝淮问潜4嬖趦?nèi)核棧中,而內(nèi)核棧這部分?jǐn)?shù)據(jù)會(huì)因回到用戶態(tài)user.sighandle()運(yùn)行而被恢復(fù)現(xiàn)場(chǎng)出棧了.保存現(xiàn)場(chǎng)/恢復(fù)現(xiàn)場(chǎng)是成雙出隊(duì)的好基友,注意有些文章說會(huì)把整個(gè)內(nèi)核棧清空,這是不對(duì)的.
第二次保存在任務(wù)結(jié)構(gòu)體中,任務(wù)來源于任務(wù)池,是內(nèi)核全局變量,常駐內(nèi)存的.兩次保存的都是user.source()運(yùn)行時(shí)現(xiàn)場(chǎng)信息,再回顧下相關(guān)的結(jié)構(gòu)體.關(guān)鍵是sig_switch_context
typedef struct { // ... sig_cb sig;//信號(hào)控制塊,用于異步通信 } LosTaskCB; typedef struct {//信號(hào)控制塊(描述符) sigset_t sigFlag; //不屏蔽的信號(hào)集 sigset_t sigPendFlag; //信號(hào)阻塞標(biāo)簽集,記錄那些信號(hào)來過,任務(wù)依然阻塞的集合.即:這些信號(hào)不能喚醒任務(wù) sigset_t sigprocmask; /* Signals that are blocked */ //任務(wù)屏蔽了哪些信號(hào) sq_queue_t sigactionq; //信號(hào)捕捉隊(duì)列 LOS_DL_LIST waitList; //等待鏈表,上面掛的是等待信號(hào)到來的任務(wù), 請(qǐng)查找 OsTaskWait(&sigcb->waitList, timeout, TRUE) 理解 sigset_t sigwaitmask; /* Waiting for pending signals */ //任務(wù)在等待哪些信號(hào)的到來 siginfo_t sigunbinfo; /* Signal info when task unblocked */ //任務(wù)解鎖時(shí)的信號(hào)信息 sig_switch_context context; //信號(hào)切換上下文, 用于保存切換現(xiàn)場(chǎng), 比如發(fā)生系統(tǒng)調(diào)用時(shí)的返回,涉及同一個(gè)任務(wù)的兩個(gè)棧進(jìn)行切換 } sig_cb;
還必須要改變?cè)蠵C/R0/R1寄存器的值.想要執(zhí)行user.sighandle(),PC寄存器就必須指向它,而R0,R1就是它的參數(shù).
信號(hào)處理完成后須回到內(nèi)核態(tài),怎么再次陷入內(nèi)核態(tài)? 答案是:__NR_sigreturn,這也是個(gè)系統(tǒng)調(diào)用.回來后還原sig_switch_context,即還原user.source()被打斷時(shí)SP/PC等寄存器的值,使其跳回到用戶棧從user.source()的被打斷處繼續(xù)執(zhí)行.
有了這三個(gè)推論,再理解下面的代碼就是吹灰之力了,涉及三個(gè)關(guān)鍵函數(shù)OsArmA32SyscallHandle,OsSaveSignalContext,OsRestorSignalContext本篇一一解讀,徹底挖透.先看信號(hào)上下文結(jié)構(gòu)體sig_switch_context.
sig_switch_context
//任務(wù)中斷上下文 #define TASK_IRQ_CONTEXT \ unsigned int R0; \ unsigned int R1; \ unsigned int R2; \ unsigned int R3; \ unsigned int R12; \ unsigned int USP; \ unsigned int ULR; \ unsigned int CPSR; \ unsigned int PC; typedef struct {//信號(hào)切換上下文 TASK_IRQ_CONTEXT unsigned int R7; //存放系統(tǒng)調(diào)用的ID unsigned int count; //記錄是否保存了信號(hào)上下文 } sig_switch_context;
保存user.source()現(xiàn)場(chǎng)的結(jié)構(gòu)體,USP,ULR代表用戶棧指針和返回地址.
CPSR寄存器用于設(shè)置CPU的工作模式,CPU有7種工作模式,具體可前往翻看
v36.xx (工作模式篇) | cpu是韋小寶,有哪七個(gè)老婆?談?wù)摰挠脩魬B(tài)(usr普通用戶)和內(nèi)核態(tài)(sys超級(jí)用戶)對(duì)應(yīng)的只是其中的兩種.二者都共用相同的寄存器.還原它就是告訴CPU內(nèi)核已切到普通用戶模式運(yùn)行.
其他寄存器沒有保存的原因是系統(tǒng)調(diào)用不會(huì)用到它們,所以不需要保存.
R7是在系統(tǒng)調(diào)用發(fā)生時(shí)用于記錄系統(tǒng)調(diào)用號(hào),在信號(hào)處理過程中,R0將獲得信號(hào)編號(hào),作為user.sighandle()的第一個(gè)參數(shù).
count記錄是否保存了信號(hào)上下文
OsArmA32SyscallHandle 系統(tǒng)調(diào)用總?cè)肟?/p>
/* The SYSCALL ID is in R7 on entry. Parameters follow in R0..R6 */ /****************************************************************** 由匯編調(diào)用,見于 los_hw_exc.s / BLX OsArmA32SyscallHandle SYSCALL是產(chǎn)生系統(tǒng)調(diào)用時(shí)觸發(fā)的信號(hào),R7寄存器存放具體的系統(tǒng)調(diào)用ID,也叫系統(tǒng)調(diào)用號(hào) regs:參數(shù)就是所有寄存器 注意:本函數(shù)在用戶態(tài)和內(nèi)核態(tài)下都可能被調(diào)用到 //MOV R0, SP @獲取SP值,R0將作為OsArmA32SyscallHandle的參數(shù) ******************************************************************/ LITE_OS_SEC_TEXT UINT32 *OsArmA32SyscallHandle(UINT32 *regs) { UINT32 ret; UINT8 nArgs; UINTPTR handle; UINT32 cmd = regs[REG_R7];//C7寄存器記錄了觸發(fā)了具體哪個(gè)系統(tǒng)調(diào)用 if (cmd >= SYS_CALL_NUM) {//系統(tǒng)調(diào)用的總數(shù) PRINT_ERR("Syscall ID: error %d !!!\n", cmd); return regs; } //用戶進(jìn)程信號(hào)處理函數(shù)完成后的系統(tǒng)調(diào)用 svc 119 #__NR_sigreturn if (cmd == __NR_sigreturn) { OsRestorSignalContext(regs);//恢復(fù)信號(hào)上下文,回到用戶棧運(yùn)行. return regs; } handle = g_syscallHandle[cmd];//拿到系統(tǒng)調(diào)用的注冊(cè)函數(shù),類似 SysRead nArgs = g_syscallNArgs[cmd / NARG_PER_BYTE]; /* 4bit per nargs */ nArgs = (cmd & 1) ? (nArgs >> NARG_BITS) : (nArgs & NARG_MASK);//獲取參數(shù)個(gè)數(shù) if ((handle == 0) || (nArgs > ARG_NUM_7)) {//系統(tǒng)調(diào)用必須有參數(shù)且參數(shù)不能大于8個(gè) PRINT_ERR("Unsupport syscall ID: %d nArgs: %d\n", cmd, nArgs); regs[REG_R0] = -ENOSYS; return regs; } //regs[0-6] 記錄系統(tǒng)調(diào)用的參數(shù),這也是由R7寄存器保存系統(tǒng)調(diào)用號(hào)的原因 switch (nArgs) {//參數(shù)的個(gè)數(shù) case ARG_NUM_0: case ARG_NUM_1: ret = (*(SyscallFun1)handle)(regs[REG_R0]);//執(zhí)行系統(tǒng)調(diào)用,類似 SysUnlink(pathname); break; case ARG_NUM_2://如何是兩個(gè)參數(shù)的系統(tǒng)調(diào)用,這里傳三個(gè)參數(shù)也沒有問題,因被調(diào)用函數(shù)不會(huì)去取用R2值 case ARG_NUM_3: ret = (*(SyscallFun3)handle)(regs[REG_R0], regs[REG_R1], regs[REG_R2]);//類似 SysExecve(fileName, argv, envp); break; case ARG_NUM_4: case ARG_NUM_5: ret = (*(SyscallFun5)handle)(regs[REG_R0], regs[REG_R1], regs[REG_R2], regs[REG_R3], regs[REG_R4]); break; default: //7個(gè)參數(shù)的情況 ret = (*(SyscallFun7)handle)(regs[REG_R0], regs[REG_R1], regs[REG_R2], regs[REG_R3], regs[REG_R4], regs[REG_R5], regs[REG_R6]); } regs[REG_R0] = ret;//R0保存系統(tǒng)調(diào)用返回值 OsSaveSignalContext(regs);//如果有信號(hào)要處理,將改寫pc,r0,r1寄存器,改變返回正常用戶態(tài)路徑,而先去執(zhí)行信號(hào)處理程序. /* Return the last value of curent_regs. This supports context switches on return from the exception. * That capability is only used with the SYS_context_switch system call. */ return regs;//返回寄存器的值 }
解讀
這是系統(tǒng)調(diào)用的總?cè)肟?所有的系統(tǒng)調(diào)用都要跑這里要統(tǒng)一處理.通過系統(tǒng)號(hào)(保存在R7),找到注冊(cè)函數(shù)并回調(diào).完成系統(tǒng)調(diào)用過程.
關(guān)于系統(tǒng)調(diào)用可查看v37.xx (系統(tǒng)調(diào)用篇) | 系統(tǒng)調(diào)用到底經(jīng)歷了什么本篇不詳細(xì)說系統(tǒng)調(diào)用過程,只說跟信號(hào)相關(guān)的部分.
OsArmA32SyscallHandle總體理解起來是被信號(hào)的保存和還原兩個(gè)函數(shù)給包夾了.注意要在運(yùn)行過程中去理解調(diào)用兩個(gè)函數(shù)的過程,對(duì)于同一個(gè)任務(wù)來說,一定是先執(zhí)行OsSaveSignalContext,第二次進(jìn)入OsArmA32SyscallHandle后再執(zhí)行OsRestorSignalContext.
看OsSaveSignalContext,由它負(fù)責(zé)保存user.source() 的上下文,其中改變了sp,r0/r1寄存器值,切到信號(hào)處理函數(shù)user.sighandle()運(yùn)行.
在函數(shù)的開頭,碰到系統(tǒng)調(diào)用號(hào)__NR_sigreturn,直接恢復(fù)信號(hào)上下文就退出了,因?yàn)檫@是要切回user.source()繼續(xù)運(yùn)行的操作.
//用戶進(jìn)程信號(hào)處理函數(shù)完成后的系統(tǒng)調(diào)用 svc 119 #__NR_sigreturn if (cmd == __NR_sigreturn) { OsRestorSignalContext(regs);//恢復(fù)信號(hào)上下文,回到用戶棧運(yùn)行. return regs; }
OsSaveSignalContext 保存信號(hào)上下文
有了上面的鋪墊,就不難理解這個(gè)函數(shù)的作用.
/********************************************** 產(chǎn)生系統(tǒng)調(diào)用時(shí),也就是軟中斷時(shí),保存用戶棧寄存器現(xiàn)場(chǎng)信息 改寫PC寄存器的值 **********************************************/ void OsSaveSignalContext(unsigned int *sp) { UINTPTR sigHandler; UINT32 intSave; LosTaskCB *task = NULL; LosProcessCB *process = NULL; sig_cb *sigcb = NULL; unsigned long cpsr; OS_RETURN_IF_VOID(sp == NULL); cpsr = OS_SYSCALL_GET_CPSR(sp);//獲取系統(tǒng)調(diào)用時(shí)的 CPSR值 OS_RETURN_IF_VOID(((cpsr & CPSR_MASK_MODE) != CPSR_USER_MODE));//必須工作在CPU的用戶模式下,注意CPSR_USER_MODE(cpu層面)和OS_USER_MODE(系統(tǒng)層面)是兩碼事. SCHEDULER_LOCK(intSave);//如有不明白前往 https://my.oschina.net/weharmony 翻看工作模式/信號(hào)分發(fā)/信號(hào)處理篇 task = OsCurrTaskGet(); process = OsCurrProcessGet(); sigcb = &task->sig;//獲取任務(wù)的信號(hào)控制塊 //1.未保存任務(wù)上下文任務(wù) //2.任何的信號(hào)標(biāo)簽集不為空或者進(jìn)程有信號(hào)要處理 if ((sigcb->context.count == 0) && ((sigcb->sigFlag != 0) || (process->sigShare != 0))) { sigHandler = OsGetSigHandler();//獲取信號(hào)處理函數(shù) if (sigHandler == 0) {//信號(hào)沒有注冊(cè) sigcb->sigFlag = 0; process->sigShare = 0; SCHEDULER_UNLOCK(intSave); PRINT_ERR("The signal processing function for the current process pid =%d is NULL!\n", task->processID); return; } /* One pthread do the share signal */ sigcb->sigFlag |= process->sigShare;//擴(kuò)展任務(wù)的信號(hào)標(biāo)簽集 unsigned int signo = (unsigned int)FindFirstSetedBit(sigcb->sigFlag) + 1; OsProcessExitCodeSignalSet(process, signo);//設(shè)置進(jìn)程退出信號(hào) sigcb->context.CPSR = cpsr; //保存狀態(tài)寄存器 sigcb->context.PC = sp[REG_PC]; //獲取被打斷現(xiàn)場(chǎng)寄存器的值 sigcb->context.USP = sp[REG_SP];//用戶棧頂位置,以便能從內(nèi)核棧切回用戶棧 sigcb->context.ULR = sp[REG_LR];//用戶棧返回地址 sigcb->context.R0 = sp[REG_R0]; //系統(tǒng)調(diào)用的返回值 sigcb->context.R1 = sp[REG_R1]; sigcb->context.R2 = sp[REG_R2]; sigcb->context.R3 = sp[REG_R3]; sigcb->context.R7 = sp[REG_R7];//為何參數(shù)不用傳R7,是因?yàn)橄到y(tǒng)調(diào)用發(fā)生時(shí) R7始終保存的是系統(tǒng)調(diào)用號(hào). sigcb->context.R12 = sp[REG_R12];//詳見 https://my.oschina.net/weharmony/blog/4967613 sp[REG_PC] = sigHandler;//指定信號(hào)執(zhí)行函數(shù),注意此處改變保存任務(wù)上下文中PC寄存器的值,恢復(fù)上下文時(shí)將執(zhí)行這個(gè)函數(shù). sp[REG_R0] = signo; //參數(shù)1,信號(hào)ID sp[REG_R1] = (unsigned int)(UINTPTR)(sigcb->sigunbinfo.si_value.sival_ptr); //參數(shù)2 /* sig No bits 00000100 present sig No 3, but 1<< 3 = 00001000, so signo needs minus 1 */ sigcb->sigFlag ^= 1ULL << (signo - 1); sigcb->context.count++; //代表已保存 } SCHEDULER_UNLOCK(intSave); }
解讀
先是判斷執(zhí)行條件,確實(shí)是有信號(hào)需要處理,有處理函數(shù).自定義處理函數(shù)是由用戶進(jìn)程安裝進(jìn)來的,所有進(jìn)程旗下的任務(wù)都共用,參數(shù)就是信號(hào)signo,注意可不是系統(tǒng)調(diào)用號(hào),有區(qū)別的.信號(hào)編號(hào)長(zhǎng)這樣.
#define SIGHUP 1 //終端掛起或者控制進(jìn)程終止 #define SIGINT 2 //鍵盤中斷(ctrl + c) #define SIGQUIT 3 //鍵盤的退出鍵被按下 #define SIGILL 4 //非法指令 #define SIGTRAP 5 //跟蹤陷阱(trace trap),啟動(dòng)進(jìn)程,跟蹤代碼的執(zhí)行 #define SIGABRT 6 //由abort(3)發(fā)出的退出指令 #define SIGIOT SIGABRT //abort發(fā)出的信號(hào) #define SIGBUS 7 //總線錯(cuò)誤 #define SIGFPE 8 //浮點(diǎn)異常 #define SIGKILL 9 //常用的命令 kill 9 123 | 不能被忽略、處理和阻塞
系統(tǒng)調(diào)用號(hào)長(zhǎng)這樣,是不是看到一些很熟悉的函數(shù).
#define __NR_restart_syscall 0 #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 #define __NR_write 4 #define __NR_open 5 #define __NR_close 6 #define __NR_waitpid 7 #define __NR_creat 8 #define __NR_link 9 #define __NR_unlink 10 #define __NR_execve 11 #define __NR_chdir 12 #define __NR_time 13 #define __NR_mknod 14 #define __NR_chmod 15 #define __NR_lchown 16 #define __NR_break 17
最后是最最最關(guān)鍵的代碼,改變pc寄存器的值,此值一變,在_osExceptSwiHdl中恢復(fù)上下文后,cpu跳到用戶空間的代碼段 user.sighandle(R0,R1) 開始執(zhí)行,即執(zhí)行信號(hào)處理函數(shù).
sp[REG_PC] = sigHandler;//指定信號(hào)執(zhí)行函數(shù),注意此處改變保存任務(wù)上下文中PC寄存器的值,恢復(fù)上下文時(shí)將執(zhí)行這個(gè)函數(shù). sp[REG_R0] = signo; //參數(shù)1,信號(hào)ID sp[REG_R1] = (unsigned int)(UINTPTR)(sigcb->sigunbinfo.si_value.sival_ptr); //參數(shù)2
OsRestorSignalContext 恢復(fù)信號(hào)上下文
/**************************************************** 恢復(fù)信號(hào)上下文,由系統(tǒng)調(diào)用之__NR_sigreturn產(chǎn)生,這是一個(gè)內(nèi)部產(chǎn)生的系統(tǒng)調(diào)用. 為什么要恢復(fù)呢? 因?yàn)橄到y(tǒng)調(diào)用的執(zhí)行由任務(wù)內(nèi)核態(tài)完成,使用的棧也是內(nèi)核棧,CPU相關(guān)寄存器記錄的都是內(nèi)核棧的內(nèi)容, 而系統(tǒng)調(diào)用完成后,需返回任務(wù)的用戶棧執(zhí)行,這時(shí)需將CPU各寄存器回到用戶態(tài)現(xiàn)場(chǎng) 所以函數(shù)的功能就變成了還原寄存器的值 ****************************************************/ void OsRestorSignalContext(unsigned int *sp) { LosTaskCB *task = NULL; /* Do not adjust this statement */ LosProcessCB *process = NULL; sig_cb *sigcb = NULL; UINT32 intSave; SCHEDULER_LOCK(intSave); task = OsCurrTaskGet(); sigcb = &task->sig;//獲取當(dāng)前任務(wù)信號(hào)控制塊 if (sigcb->context.count != 1) {//必須之前保存過,才能被恢復(fù) SCHEDULER_UNLOCK(intSave); PRINT_ERR("sig error count : %d\n", sigcb->context.count); return; } process = OsCurrProcessGet();//獲取當(dāng)前進(jìn)程 sp[REG_PC] = sigcb->context.PC;//指令寄存器 OS_SYSCALL_SET_CPSR(sp, sigcb->context.CPSR);//重置程序狀態(tài)寄存器 sp[REG_SP] = sigcb->context.USP;//用戶棧堆棧指針, USP指的是 用戶態(tài)的堆棧,即將回到用戶棧繼續(xù)運(yùn)行 sp[REG_LR] = sigcb->context.ULR;//返回用戶棧代碼執(zhí)行位置 sp[REG_R0] = sigcb->context.R0; sp[REG_R1] = sigcb->context.R1; sp[REG_R2] = sigcb->context.R2; sp[REG_R3] = sigcb->context.R3; sp[REG_R7] = sigcb->context.R7; sp[REG_R12] = sigcb->context.R12; sigcb->context.count--; //信號(hào)上下文的數(shù)量回到減少 process->sigShare = 0; //回到用戶態(tài),信號(hào)共享清0 OsProcessExitCodeSignalClear(process);//清空進(jìn)程退出碼 SCHEDULER_UNLOCK(intSave); }
解讀
在信號(hào)處理函數(shù)完成之后,內(nèi)核會(huì)觸發(fā)一個(gè)__NR_sigreturn的系統(tǒng)調(diào)用,又陷入內(nèi)核態(tài),回到了OsArmA32SyscallHandle.
恢復(fù)的過程很簡(jiǎn)單,把之前保存的信號(hào)上下文恢復(fù)到內(nèi)核棧sp開始位置,數(shù)據(jù)在棧中的保存順序可查看 用棧方式篇 ,最重要的看這幾句.
sp[REG_PC] = sigcb->context.PC;//指令寄存器 sp[REG_SP] = sigcb->context.USP;//用戶棧堆棧指針, USP指的是 用戶態(tài)的堆棧,即將回到用戶棧繼續(xù)運(yùn)行 sp[REG_LR] = sigcb->context.ULR;//返回用戶棧代碼執(zhí)行位置
注意這里還不是真正的切換上下文,只是改變內(nèi)核棧中現(xiàn)有的數(shù)據(jù).這些數(shù)據(jù)將還原給寄存器.USP和ULR指向的是用戶棧的位置.一旦PC,USP,ULR從棧中彈出賦給寄存器.才真正完成了內(nèi)核棧到用戶棧的切換.回到了user.source()繼續(xù)運(yùn)行.
真正的切換匯編代碼如下,都已添加注釋,在保存和恢復(fù)上下文中夾著OsArmA32SyscallHandle
@ Description: Software interrupt exception handler _osExceptSwiHdl: @軟中斷異常處理,注意此時(shí)已在內(nèi)核棧運(yùn)行 @保存任務(wù)上下文(TaskContext) 開始... 一定要對(duì)照TaskContext來理解 SUB SP, SP, #(4 * 16) @先申請(qǐng)16個(gè)??臻g單元用于處理本次軟中斷 STMIA SP, {R0-R12} @TaskContext.R[GEN_REGS_NUM] STMIA從左到右執(zhí)行,先放R0 .. R12 MRS R3, SPSR @讀取本模式下的SPSR值 MOV R4, LR @保存回跳寄存器LR AND R1, R3, #CPSR_MASK_MODE @ Interrupted mode 獲取中斷模式 CMP R1, #CPSR_USER_MODE @ User mode 是否為用戶模式 BNE OsKernelSVCHandler @ Branch if not user mode 非用戶模式下跳轉(zhuǎn) @ 當(dāng)為用戶模式時(shí),獲取SP和LR寄出去值 @ we enter from user mode, we need get the values of USER mode r13(sp) and r14(lr). @ stmia with ^ will return the user mode registers (provided that r15 is not in the register list). MOV R0, SP @獲取SP值,R0將作為OsArmA32SyscallHandle的參數(shù) STMFD SP!, {R3} @ Save the CPSR 入棧保存CPSR值 => TaskContext.regPSR ADD R3, SP, #(4 * 17) @ Offset to pc/cpsr storage 跳到PC/CPSR存儲(chǔ)位置 STMFD R3!, {R4} @ Save the CPSR and r15(pc) 保存LR寄存器 => TaskContext.PC STMFD R3, {R13, R14}^ @ Save user mode r13(sp) and r14(lr) 從右向左 保存 => TaskContext.LR和SP SUB SP, SP, #4 @ => TaskContext.resved PUSH_FPU_REGS R1 @保存中斷模式(用戶模式) @保存任務(wù)上下文(TaskContext) 結(jié)束 MOV FP, #0 @ Init frame pointer CPSIE I @開中斷,表明在系統(tǒng)調(diào)用期間可響應(yīng)中斷 BLX OsArmA32SyscallHandle /*交給C語言處理系統(tǒng)調(diào)用,參數(shù)為R0,指向TaskContext的開始位置*/ CPSID I @執(zhí)行后續(xù)指令前必須先關(guān)中斷 @恢復(fù)任務(wù)上下文(TaskContext) 開始 POP_FPU_REGS R1 @彈出FPU值給R1 ADD SP, SP,#4 @ 定位到保存舊SPSR值的位置 LDMFD SP!, {R3} @ Fetch the return SPSR 彈出舊SPSR值 MSR SPSR_cxsf, R3 @ Set the return mode SPSR 恢復(fù)該模式下的SPSR值 @ we are leaving to user mode, we need to restore the values of USER mode r13(sp) and r14(lr). @ ldmia with ^ will return the user mode registers (provided that r15 is not in the register list) LDMFD SP!, {R0-R12} @恢復(fù)R0-R12寄存器 LDMFD SP, {R13, R14}^ @ Restore user mode R13/R14 恢復(fù)用戶模式的R13/R14寄存器 ADD SP, SP, #(2 * 4) @定位到保存舊PC值的位置 LDMFD SP!, {PC}^ @ Return to user 切回用戶模式運(yùn)行 @恢復(fù)任務(wù)上下文(TaskContext) 結(jié)束
編輯:hfy
-
寄存器
+關(guān)注
關(guān)注
31文章
5253瀏覽量
119205 -
cpu
+關(guān)注
關(guān)注
68文章
10702瀏覽量
209373 -
信號(hào)處理
+關(guān)注
關(guān)注
48文章
969瀏覽量
102989
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論