PowerPC處理器上vxWorks 異常和中斷處理過(guò)程解析
異常和中斷處理過(guò)程是每個(gè)體系結(jié)構(gòu)和OS都要面對(duì)的重要問(wèn)題,本文從硬件角度以及軟件角度來(lái)分析PowerPc這個(gè)過(guò)程,文字水平有限,將就著看。
PowerPC定義了十幾種異常,其中常見(jiàn)的如DataTLB miss, Instruction TLB miss, external input....每一個(gè)都有固定的地址(ivorn),從物理地址0x100開(kāi)始.0x500是中斷入口,0xe00是DTLBmiss,0xf00是itlbmiss入口.首先整體了解一下中斷發(fā)生時(shí)硬件/軟件處理流程,硬件會(huì)做什么,OS軟件要做什么。
PowerPC 中斷的硬件處理流程:(ePAPR第四章)
1. 把正在執(zhí)行的指令序列下一條指令地址保存到中斷寄存器SRR0(Save/RestoreRegiser 0)。
2. 把當(dāng)前 MSR 的內(nèi)容保存到 SRR1。
3. 把 MSR 某些比特置為 0,如MSR[SPE,WE,EE,PR,FP,FE0,FE1,IS,DS]are 0 by all interrupts.
【注】:PowerPC有兩種執(zhí)行模式,分別為用戶(hù)模式(UserMode) 和特權(quán)模式(SupervisorMode), MSR[PR]=0 表示特權(quán)模式。Linux內(nèi)核運(yùn)行在特權(quán)模式,而普通程序運(yùn)行在用戶(hù)模式。vxWorks全部運(yùn)行在特權(quán)模式。
1. 在新的 MSR 狀態(tài)下,從中斷向量偏移處開(kāi)始指令讀取和執(zhí)行。
2. 外部中斷處理結(jié)束時(shí),必須通過(guò) rfi 指令返回。
3. rfi 的執(zhí)行,會(huì)把 SRR1 的內(nèi)容恢復(fù)到 MSR, 并從 SRR0 所保存的地址處繼續(xù)執(zhí)行。
PowerPC Linux (軟件)中斷處理程序
異常向量ExternalInput處的處理程序主要分為以下幾個(gè)步驟:
1. NORMAL_EXCEPTION_PROLOG宏
建立用戶(hù)中斷處理程序的棧幀,并把一些寄存器的值保存在棧幀中,它們?cè)跅胁季钟蓅tructpt_regs 定義。
2. 執(zhí)行 do_IRQ
在irq_enter()函數(shù)中更新preempt_count。 讀MPIC的IACK寄存器,通知MPIC開(kāi)始處理該中斷,同時(shí)獲取MPIC中斷號(hào),映射為軟件中斷號(hào),找到并運(yùn)行注冊(cè)在該軟件中斷號(hào)上的用戶(hù)中斷處理程序,最后寫(xiě)MPIC的EOI寄存器,通知MPIC中斷處理結(jié)束。
在irq_exit()中更新preempt_count,調(diào)用invoke_softirq()處理pending的軟中斷。
3. ret_from_except
某些條件滿(mǎn)足時(shí),進(jìn)行進(jìn)程調(diào)度和信號(hào)處理,最后調(diào)用rfi指令返回。
PowerPC vxWorks(軟件)中斷處理程序
VxWorks的處理流程大體上與linux一樣,只是在代碼實(shí)現(xiàn)細(xì)節(jié)上存在些區(qū)別。
代碼的調(diào)用路徑:usrInit ->excVecInit ->excVecConnectCommon
代碼實(shí)現(xiàn)過(guò)程:
把一段機(jī)器碼excConnectCode[]拷貝到每個(gè)異常vector的入口地址處(excVecConnectCommon里實(shí)現(xiàn)),再重新計(jì)算異常處理函數(shù)的地址并覆蓋通用處理函數(shù)地址。
[cpp] view plain copyLOCAL INSTR excConnectCode[]=
{
/* data word byte opcode operands */
0x7c7343a6, /* 0 0x00 mtspr SPRG3, p0 */
0x7c6000a6, /* 1 0x04 mfmsr p0 */
0x546303da, /* 2 0x08 rlwinm p0,p0,0,15,13 clear MSR[CE] */
0x7c600124, /* 3 0x0c mtmsr p0 */
0x60000000, /* 4 0x10 nop */
/* If either ofthe above, add 4 words/0x10bytes to following offsets */
0x7c6802a6, /* 1 0x04 mflr p0 */
0x48000001, /* 2(6) 0x08/18 bl xxxEnt */
0x38610000, /* 3 0x0c addi r3, sp, 0 */
0x9421fff0, /* 4 0x10 stwu sp, -FRAMEBASESZ(sp) */
0x48000001, /* 5(9) 0x14/24 bl xxxHandler */
0x38210010, /* 6 0x18 addi sp, sp, FRAMEBASESZ */
0x48000001 /* 7(11) 0x1c/2c bl xxxExit */
};
整個(gè)過(guò)程中最關(guān)鍵的就是這段機(jī)器碼數(shù)組,一共12條指令。
[cpp] view plain copy0x7c7343a6, /* 0 0x00 mtspr SPRG3, p0 */
0x7c6000a6, /* 1 0x04 mfmsr p0 */
0x546303da, /* 2 0x08 rlwinm p0,p0,0,15,13 clear MSR[CE] */
0x7c600124, /* 3 0x0c mtmsr p0 */
0x60000000, /* 4 0x10 nop
首先清MSR[CE],防止在保存中斷context時(shí)被CE中斷打斷
[cpp] view plain copy0x7c6802a6, /* 1 0x04 mflr p0 */
0x48000001, /* 2(6) 0x08/18 bl xxxEnt */
0x38610000, /* 3 0x0c addi r3, sp, 0 */
0x9421fff0, /* 4 0x10 stwu sp, -FRAMEBASESZ(sp) */
0x48000001, /* 5(9) 0x14/24 bl xxxHandler */
0x38210010, /* 6 0x18 addi sp, sp, FRAMEBASESZ */
0x48000001 /* 7(11) 0x1c/2c bl xxxExit */
xxxEnt是intEntorexcEnt.
xxxHandler是 excIntHandle or excExcHandle
xxxExit是intExitor excExit.
其中intEnt/intExit,excEnt/excExit是通用中斷/異常入口和出口函數(shù),中斷進(jìn)前者,異常進(jìn)后者,主要作用是保存context以及恢復(fù)context,設(shè)置中斷棧,保存易失的寄存器變量。。。。。
excIntHandle/excExcHandle也是通用處理函數(shù),主要作用是打印下異常信息,對(duì)于關(guān)鍵異常或者中斷的話(huà),都有自己?jiǎn)为?dú)的處理函數(shù),會(huì)在接下來(lái)的初始化過(guò)程中逐個(gè)把xxxHandler換成專(zhuān)用的處理函數(shù),實(shí)現(xiàn)的方法是直接修改機(jī)器碼后16bit。
MMU初始化的時(shí)候替換excExcHandle為mmuPpcDataTlbMissHandler/mmuPpcInstTlbMissHandler
MPIC初始化的時(shí)候會(huì)用vxbMpicIntHandler替換excIntHandle來(lái)調(diào)用ISR
WD/Timer初始化的時(shí)候也會(huì)替換成自己的Handler
至此,真正中斷/異常來(lái)的時(shí)候會(huì)執(zhí)行intEnt->vxbMpicIntHandler->intExit
下面的代碼實(shí)現(xiàn)是intEnt,可以看看里面都做了哪些事情
[cpp] view plain copy864 /*
865 *ThePowerPC Family doesn't support the notion of an interrupt
866 * stackinhardware. To avoid having to allowspacefor interrupt
867 *handlingin each task stack, this stub switches from the task
868 * stacktothe interrupt stack by changing the value of the SP(R1).
869 * Iftheinterrupt is nested, we don't need to switch stacks but
870 * wedoneed to check for overflow.[!--empirenews.page--]
871 */
893 _PPC_PER_CPU_VALUE_GET(p0,vxIntStackBase)
894 stwu sp, -_PPC_ESF_STK_SIZE(p0) /* carve stack */
895 mr sp, p0 /* switch tointerruptstack */
/*
1251 * The critical status are saved atthisstage. The interrupt should
1252 *beenabled as soon as possible to reduce the interrupt latency.
1253 *However,there is only one mask bit on PowerPC. It is at the
1254 *interruptcontroller level to set mask for each individual
1255 *interrupt.
1256 * Thus,wesave task's register first, then call interrupt controller
1257 *routineto decide if the interrupt should be re-enabled or not.
1258 */
mfspr p0, XER /* load XER to P0 */
1261 stw p0, _PPC_ESF_XER(sp) /* save XER to the stack */
1262
1263 mfspr p0, CTR /* load CTR to P0 */
1264 stw p0, _PPC_ESF_CTR(sp) /* save CTR to the stack */
1265
1266#if (CPU==PPC601)
1267 mfspr p0, MQ /* load MQ to P0 */
1268 stw p0, _PPC_ESF_MQ(sp) /* save MQ to the stack */
1269#endif /*(CPU==PPC601)*/
1270#if (defined(_WRS_CONFIG_PPC_E500))
1271#ifdef _WRS_SPE_SUPPORT
1272 mfspr p0, SPEFSCR /* load SPEFSCR to P0 */
1273 stw p0, _PPC_ESF_SPEFSCR(sp) /* save SPEFSCR to the stack */
1274#endif /*_WRS_SPE_SUPPORT */
1275#endif /*(defined(_WRS_CONFIG_PPC_E500)) */
1276
1277 stw r0, _PPC_ESF_R0(sp) /* save general register 0 */
1278 stw r2, _PPC_ESF_R2(sp) /* save general register 2 */
1279
1280 stw p2, _PPC_ESF_P2(sp) /* save general register 5 */
1281 stw p3, _PPC_ESF_P3(sp) /* save general register 6 */
1282 stw p4, _PPC_ESF_P4(sp) /* save general register 7 */
1283 stw p5, _PPC_ESF_P5(sp) /* save general register 8 */
1284 stw p6, _PPC_ESF_P6(sp) /* save general register 9 */
1285 stw p7, _PPC_ESF_P7(sp) /* save general register 10 */
1286
1287 stw r11,_PPC_ESF_R11(sp) /* savegeneral register 11 */
1288 stw r12, _PPC_ESF_R12(sp) /* save general register 12 */
1289 stw r13, _PPC_ESF_R13(sp) /* save general register 13 */
1290 beqlr+ cr4 /* return to vector unlessstackoverflow */