uboot中的中断macro宏
目錄
- uboot中的中斷macro宏
- 引入
- 內存分配
- 流程概覽
- 普通中斷
- 保存現場
- 中斷函數打印具體寄存器
- 恢復現場
- 軟中斷
- 空間獲取
- 保存現場
- 附錄速記
- 疑惑待解
uboot中的中斷macro宏
引入
以前因為uboot的目的只是引導linux,沒有去看關于中斷相關的代碼,這兩天重新回顧看了下Uboot中start.S源碼的指令級的詳盡解析中關于uboot1.6的分析,看了下中斷章節,記錄一下.原文已經更新到V1.9,網上很多流傳的是1.6的,本文作為對齊章節的補充.這里要先明確不同狀態下都有哪些獨立的寄存器
內存分配
先來看下內部的sp是怎么設置的,這里的用戶棧加上中斷棧等為128k,是在后面代碼分析得出來的
/* Set up the stack */ stack_setup:ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */sub r0, r0, #CFG_MALLOC_LEN /* malloc area */sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQsub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endifsub sp, r0, #12 /* leave 3 words for abort-stack */具體的內存棧IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;流程概覽
先來看下整體的流程的代碼
普通中斷模式 ---------------------------------------------------------------------------- 正常程序 ↓ 中斷 ↓ 硬件切換到中斷SP ↓ 設置SP_irq到中斷棧頂 ↓ 保存r0-r12,使用stmdb r8, {sp, lr}^ 保存 用戶模式的sp,lr}^ 直接保存 spsr 來保存用戶的cpsr str lr, [r8, #0] ↓ 將中斷棧sp傳遞給函數,調用打印 ↓ 恢復r0-r14(lr),這個lr是用戶函數本身的返回 恢復S_PC 到現在的lr 設置pc=lr 返回用戶函數 subs pc, lr, #4 sub+s表示更新cpsr其他異常模式 ---------------------------------------------------------------------------- 正常程序 ↓ 異常 ↓ 切換到異常的sp寄存器,設置到用戶棧的棧底 ↓ 保存 當前的lr=用戶的pc 保存 用戶的cpsr ↓ 切換到系統模式 ↓ 切換到用戶模式的sp寄存器,這里的系統模式和用戶模式是公用寄存器的 計算用戶棧底到r2,從棧底取出上面存的 用戶的pc 和 用戶的cpsr ↓ 當前的sp指向當前用戶棧指針 ↓ 保存當前的lr 當前的lr這里就是用戶模式的lr 保存用戶的pc cpsr 保存這個用戶棧的指針-----實際也是我們保存這些寄存器 r0-r12,lr,pc,cpsr..的地址 ↓ 將棧sp傳遞給函數,調用打印保存現場,保存用戶態的寄存器到異常的棧中
/** use bad_save_user_regs for abort/prefetch/undef/swi ...* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling*/.macro bad_save_user_regssub sp, sp, #S_FRAME_SIZEstmia sp, {r0 - r12} @ Calling r0-r12ldr r2, _armboot_startsub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stackldmia r2, {r2 - r3} @ get pc, cpsradd r0, sp, #S_FRAME_SIZE @ restore sp_SVCadd r5, sp, #S_SPmov r1, lrstmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsrmov r0, sp.endm.macro irq_save_user_regssub sp, sp, #S_FRAME_SIZEstmia sp, {r0 - r12} @ Calling r0-r12add r8, sp, #S_PCstmdb r8, {sp, lr}^ @ Calling SP, LRstr lr, [r8, #0] @ Save calling PCmrs r6, spsrstr r6, [r8, #4] @ Save CPSRstr r0, [r8, #8] @ Save OLD_R0mov r0, sp.endm.macro irq_restore_user_regsldmia sp, {r0 - lr}^ @ Calling r0 - lrmov r0, r0ldr lr, [sp, #S_PC] @ Get PCadd sp, sp, #S_FRAME_SIZEsubs pc, lr, #4 @ return & move spsr_svc into cpsr.endm.macro get_bad_stackldr r13, _armboot_start @ setup our mode stacksub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stackstr lr, [r13] @ save caller lr / spsrmrs lr, spsrstr lr, [r13, #4]mov r13, #MODE_SVC @ prepare SVC-Mode@ msr spsr_c, r13msr spsr, r13mov lr, pcmovs pc, lr.endm.macro get_irq_stack @ setup IRQ stackldr sp, IRQ_STACK_START.endm.macro get_fiq_stack @ setup FIQ stackldr sp, FIQ_STACK_START.endm具體的中斷函數
/** exception handlers*/.align 5 undefined_instruction:get_bad_stackbad_save_user_regsbl do_undefined_instruction.align 5 software_interrupt:get_bad_stackbad_save_user_regsbl do_software_interrupt.align 5 prefetch_abort:get_bad_stackbad_save_user_regsbl do_prefetch_abort.align 5 data_abort:get_bad_stackbad_save_user_regsbl do_data_abort.align 5 not_used:get_bad_stackbad_save_user_regsbl do_not_used#ifdef CONFIG_USE_IRQ.align 5 irq:get_irq_stackirq_save_user_regsbl do_irqirq_restore_user_regs.align 5 fiq:get_fiq_stack/* someone ought to write a more effiction fiq_save_user_regs */irq_save_user_regsbl do_fiqirq_restore_user_regs#else.align 5 irq:get_bad_stackbad_save_user_regsbl do_irq.align 5 fiq:get_bad_stackbad_save_user_regsbl do_fiq#endif普通中斷
先來分析普通的中斷函數
.align 5 ;2^5=32字節對齊 irq:get_irq_stack ;ldr sp, IRQ_STACK_START ;獲得irq的棧irq_save_user_regsbl do_irqirq_restore_user_regs保存現場
這里使用stmdb r8, {sp, lr}^保存用戶態的寄存器
ldr sp, IRQ_STACK_START ;獲得irq的棧.macro irq_save_user_regssub sp, sp, #S_FRAME_SIZE;sp 空出S_FRAME_SIZE =72大小;這里的52個字節空間的分配如下:@@ IRQ stack frame.@#define S_FRAME_SIZE 72#define S_OLD_R0 68#define S_PSR 64#define S_PC 60#define S_LR 56#define S_SP 52@ 這里沒有空余的空間#define S_IP 48#define S_FP 44#define S_R10 40#define S_R9 36#define S_R8 32#define S_R7 28#define S_R6 24#define S_R5 20#define S_R4 16#define S_R3 12#define S_R2 8#define S_R1 4#define S_R0 0#define MODE_SVC 0x13#define I_BIT 0x80;到這里的時候,sp=sp(頂)-S_FRAME_SIZEstmia sp, {r0 - r12} @ Calling r0-r12;stmia 是;IA 每次傳送后地址加1;;IB 每次傳送前地址加1;;DA 每次傳送后地址減1;;DB 每次傳送前地址減1;;FD 滿遞減堆棧;;ED 空遞減堆棧;;FA 滿遞增堆棧;;EA 空遞增堆棧;;從[sp-S_FRAME_SIZE]的地址開始,存儲r0~r12;由于上面的指令 sp沒有!,所以sp本身最后不變,依然是 sp=sp(頂)-S_FRAME_SIZEadd r8, sp, #S_PC;這里將 sp值指向 S_PC 其實就是接著上面的存,這里直接指向的就是沒有存過的sp;但是本身的sp依然指向 sp=sp(頂)-S_FRAME_SIZEstmdb r8, {sp, lr}^ @ Calling SP, LR;這里因為有^ 所以訪問的是用戶模式的寄存器;這個是 DB 每次傳送前地址減1; 也就是將 sp用戶模式 lr用戶模式存儲; [r8-1]=sp; [r8-2]=lr;但是沒有使用! 也就是說r8依然是S_PC,也就是保存了S_PC 也就是用戶函數的pcstr lr, [r8, #0] @ Save calling PCmrs r6, spsrstr r6, [r8, #4] @ Save CPSR; 把spsr 存到 下一個單元 str r0, [r8, #8] @ Save OLD_R0;把r0 存到下一個單元mov r0, sp ; 這里的sp依然是sp=sp(頂)-S_FRAME_SIZE ,也就是我們保存現場的東西的頭指針 傳遞個r0 ; 準備給c語言傳遞參數.endm也就是說最終的地址分配是這樣的
struct pt_regs {long uregs[18]; };#define ARM_cpsr uregs[16] #define ARM_pc uregs[15] #define ARM_lr uregs[14] #define ARM_sp uregs[13] #define ARM_ip uregs[12] #define ARM_fp uregs[11] #define ARM_r10 uregs[10] #define ARM_r9 uregs[9] #define ARM_r8 uregs[8] #define ARM_r7 uregs[7] #define ARM_r6 uregs[6] #define ARM_r5 uregs[5] #define ARM_r4 uregs[4] #define ARM_r3 uregs[3] #define ARM_r2 uregs[2] #define ARM_r1 uregs[1] #define ARM_r0 uregs[0] #define ARM_ORIG_r0 uregs[17]中斷函數打印具體寄存器
void do_irq (struct pt_regs *pt_regs) { #if defined (CONFIG_USE_IRQ) && defined (CONFIG_ARCH_INTEGRATOR)/* ASSUMED to be a timer interrupt *//* Just clear it - count handled in *//* integratorap.c */*(volatile ulong *)(CFG_TIMERBASE + 0x0C) = 0; #elseprintf ("interrupt request\n");show_regs (pt_regs);bad_mode (); /* 死循環*/ #endif } //-------------------------------------------------------------- void show_regs (struct pt_regs *regs) {unsigned long flags;const char *processor_modes[] = {"USER_26", "FIQ_26", "IRQ_26", "SVC_26","UK4_26", "UK5_26", "UK6_26", "UK7_26","UK8_26", "UK9_26", "UK10_26", "UK11_26","UK12_26", "UK13_26", "UK14_26", "UK15_26","USER_32", "FIQ_32", "IRQ_32", "SVC_32","UK4_32", "UK5_32", "UK6_32", "ABT_32","UK8_32", "UK9_32", "UK10_32", "UND_32","UK12_32", "UK13_32", "UK14_32", "SYS_32",};//#define condition_codes(regs) ((regs)->ARM_cpsr & (CC_V_BIT|CC_C_BIT|CC_Z_BIT|CC_N_BIT))flags = condition_codes (regs);//#define PCMASK 0//#define pc_pointer(v) ((v) & ~PCMASK)//#define instruction_pointer(regs) (pc_pointer((regs)->ARM_pc))printf ("pc : [<%08lx>] lr : [<%08lx>]\n""sp : %08lx ip : %08lx fp : %08lx\n",instruction_pointer (regs),regs->ARM_lr, regs->ARM_sp, regs->ARM_ip, regs->ARM_fp);printf ("r10: %08lx r9 : %08lx r8 : %08lx\n",regs->ARM_r10, regs->ARM_r9, regs->ARM_r8);printf ("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4);printf ("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0);printf ("Flags: %c%c%c%c",flags & CC_N_BIT ? 'N' : 'n',flags & CC_Z_BIT ? 'Z' : 'z',flags & CC_C_BIT ? 'C' : 'c', flags & CC_V_BIT ? 'V' : 'v');printf (" IRQs %s FIQs %s Mode %s%s\n",interrupts_enabled (regs) ? "on" : "off",fast_interrupts_enabled (regs) ? "on" : "off",processor_modes[processor_mode (regs)],thumb_mode (regs) ? " (T)" : ""); }恢復現場
這里就是恢復r0-r12,返回到lr執行
.macro irq_restore_user_regs;在irq_save_user_regs 中,sp=sp(頂)-S_FRAME_SIZE;也就是指向了保存現場區域ldmia sp, {r0 - lr}^ @ Calling r0 - lrmov r0, r0ldr lr, [sp, #S_PC] @ Get PCadd sp, sp, #S_FRAME_SIZE ;這里恢復sp到頂部subs pc, lr, #4 @ return & move spsr_svc into cpsr; 這里返回地址是 lr-4,具體是手冊確定的;注意這里有個 sub+s 表示更新cpsr.endm其他的返回地址如下
BL MOV PC, R14 SWI MOVS PC, R14_svc UDEF MOVS PC, R14_und FIQ SUBS PC, R14_fiq, #4 IRQ SUBS PC, R14_irq, #4 PABT SUBS PC, R14_abt, #4 DABT SUBS PC, R14_abt, #8軟中斷
軟中斷和普通中斷應該只是寄存器不太一樣,所以流程上是一樣的
.align 5 software_interrupt:get_bad_stackbad_save_user_regsbl do_software_interrupt空間獲取
這里的棧分配有點不一樣從代碼上看,有著模式切換,切換到用戶模式后,將sp和cpsr保存到棧底
.macro get_bad_stack; ldr r13, _armboot_start @ setup our mode stacksub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack; 這里感覺是一個未知的空間,假設為x 起始str lr, [r13] @ save caller lr / spsr; 先存lr,這個lr是用戶的pc調用者mrs lr, spsrstr lr, [r13, #4];再存 spsrmov r13, #MODE_SVC @ prepare SVC-Mode@ msr spsr_c, r13msr spsr, r13;設置了 spsr=MODE_SVCmov lr, pc ;把pc值 實際是endm的值給lrmovs pc, lr ;把endm后面的值給pc 總的來說還是執行 endm 的語句,;也就是這兩句話 本身對于程序流程沒有什么影響,但是能夠更新spsr;注意 接下去切換到了系統模式,sp會是用戶模式的sp了 .endm也就是我們在一段未知的空間,先存儲了lr,spsr
關于這里的?mov lr, pc ,movs pc, lr參考這里,用這兩條指令可以更新cpsr的標志位。
假設100:mov lr, pc 104:movs pc,lr 108:xxx mov lr, pc實際的pc其實等于pc+8即lr=108,后面的movs pc,lr就是跳轉到108運行。關于這里的movs指令解釋如下,在ARM指令集E004armproc.chm
任何帶 S 位設置的到 R15 的 32-bit 寫(MOVS、ORRS、TEQP、LDM...^) 將傳送當前模式的 SPSR 到 CPSR 中。例如,假定我們在 irq_32 模式下: MOVS PC, R14 將復制 R14 到 PC,并接著復制 SPSR_IRQ32 到 CPSR。 這在 USR 模式下不是非常有用因為它沒有 SPSR!保存現場
這里
.macro get_bad_stack ; ldr r13, _armboot_start @ setup our mode stack sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN) sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack; 這里感覺是一個未知的空間,假設為x 起始 str lr, [r13] @ save caller lr / spsr ; 先存lr ;ARM處理器相應異常時,會自動完成將當前的PC保存到LR寄存器。 ;也就是說 這個lr就是返回用戶的地址mrs lr, spsr str lr, [r13, #4] ;再存 用戶的cpsrmov r13, #MODE_SVC @ prepare SVC-Mode @ msr spsr_c, r13 msr spsr, r13 ;設置了 spsr=MODE_SVCmov lr, pc ;把pc值 實際是endm的值給lr 也就是下面的lablexxx movs pc, lr ;把endm后面的值給pc 總的來說還是執行 endm 的語句, ;也就是這兩句話 本身對于程序流程沒有什么影響,但是能夠更新spsr;注意 接下去切換到了系統模式,sp會是用戶模式的sp了 .endm------------------------------------------------------------------------------ 接下去就切換到用戶模式了 sp 切換到用戶模式的sp lr這里應該是切換回用戶自己原來的lr了,也就是說切換模式并不會賦值lrlablexxx .macro bad_save_user_regs;這里的sp是用戶模式的sp了,也就是在用戶區域開辟一個 S_FRAME_SIZE 的空間 sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ Calling r0-r12 ;這里的 r0~r12 是一樣的ldr r2, _armboot_start sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN) sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack ; r2指向3word ldmia r2, {r2 - r3} @ get pc, cpsr , ; 在上面的函數get_bad_stack ,我們在這個地方存儲了`lr,spsr` 這個是用戶態的 pc cpsr ;IA 每次傳送后地址加1add r0, sp, #S_FRAME_SIZE @ restore sp_SVC ;sp 回到用戶原來的sp尾巴add r5, sp, #S_SP ;r5 這個區用來存 進入異常前的用戶原來的spmov r1, lr;到這里為止 ; r0 =sp, #S_FRAME_SIZE =進入異常前的用戶原來的sp ; r1 =lr 這個lr應該就是用戶模式的lr,切換回用戶模式后,并沒有對他操作過 ; r2 =pc 這個是進入異常前的用戶原來的pc斷點 ; r3 =cpsr 用戶的cpsrstmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr mov r0, sp ;這里的sp 實際上是用戶棧 .endm附錄速記
手動設置cpsr到中斷模式 應該是不會跳轉到中斷向量表,只是模式變了
任何帶 S 位設置的到 R15 的 32-bit 寫(MOVS、ORRS、TEQP、LDM...^) 將傳送當前模式的 SPSR 到 CPSR 中。
疑惑待解
從我畫的圖中可以看到,作者的意思應該是保留12個字節給bad_stack模式用,但實際代碼是存在棧底的,是否是我哪里理解錯了
轉載:https://www.cnblogs.com/zongzi10010/p/10443614.html
總結
以上是生活随笔為你收集整理的uboot中的中断macro宏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单BootLoader
- 下一篇: uboot2012(一)分析重定位