Non Maskable Interrupt(NMI)不可屏蔽中斷 學習總結


CRBNMI.C里面有function

         VOID CRBGpi8SmiHandler (

    IN EFI_HANDLE                   DispatchHandle,

    IN EFI_SMM_GPI_DISPATCH_CONTEXT *DispatchContext )

{

                       // Porting if needed

}

 

為什么通過相應的GPIO會產生一個SMI信號?

l  The corresponding GPI must be routed in the GPI_ROUT register to cause an SMI.

l  MmPci32(LPC_BUS, LPC_DEVICE, LPC_FUNC, ICH_REG_LPC_GPI_ROUT) |= 1 << (2 * GpiNo);

將配對的GPI在GPI_POUT中設置,SMI信號就可以rout到相應的GPIO

 

軟件觸發NMI的流程:

在SMI的Handle處理程序中設置相應的寄存器來觸發NMI,流程如下(參考SBSMI.C里的SBGpi14SmiHandler() ):

         TCO_BASE_ADDRESS=0x0460

         ICH_IOREG_TCO1_CNT=0x08

ICH_IOREG_TCO1_STS=0x04

ALIASED_NMI_EN_PORT=0x74

NMI_EN_PORT=0x70

 

1、  Read the NMI2SMI_EN bit, save it for future restore:                            

Save_Nmi2Smi_En = IoRead8(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_CNT + 1);

 

2、  Set the NMI2SMI_EN bit to 0

Data8 = (UINT8)(Save_Nmi2Smi_En & 0xFD);

IoWrite8(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_CNT + 1, Data8);

 

3、  Enable NMI_EN

    Save_Port70 = IoRead8(ALIASED_NMI_EN_PORT);

    Data8 = (UINT8)(Save_Port70 & 0x7F);

     IoWrite8(NMI_EN_PORT, Data8);

 

4、  Set NMI_NOW = 1

    Data8 = IoRead8(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_CNT + 1);

    Data8 = (UINT8) (Data8 | 0x01);

IoWrite8(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_CNT + 1, Data8);

 

5、  Clear NMI_NOW = 0 by writing 1 to NMI_NOW bit

    Data8 = IoRead8(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_CNT + 1);

    Data8 = (UINT8) (Data8 | 0x01);

IoWrite8(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_CNT + 1, Data8);

 

6、  Restore NMI2SMI_EN

IoWrite8(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_CNT + 1, Save_Nmi2Smi_En);

 

7、  Clear the MCHSERR_STS bit, bit 12

    Data16 = IoRead16(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_STS);

    Data16 = (UINT8) (Data16 | 0x1000);

IoWrite16(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_STS, Data16);

 

8、  Clear the NMI2SMI_STS bit if set

Data16 = IoRead16(TCO_BASE_ADDRESS + ICH_IOREG_TCO1_STS);

    if (Data16 & 0x0001) {

        // check port 0x61

        Data8 = IoRead8(NMI_SC_PORT);

        if (Data8 & 0x80) {

            Data8 = IoRead8(NMI_SC_PORT);

            Data8 = (UINT8) (Data8 | 0x04);

            Data8 = (UINT8) (Data8 & 0x0F);

            IoWrite8 (NMI_SC_PORT, Data8);

            Data8 = (UINT8) (Data8 & 0x0B);

            IoWrite8 (NMI_SC_PORT, Data8);

        }

}

 

9、  Restore NMI_EN

IoWrite8(NMI_EN_PORT, Save_Port70);     

NMI_EN是從0x74端口讀,從0x70端口寫

 

對怎樣執行NMI中斷處理程序的理解

當NMI被觸發以后,interrupt controller(8259)會將NMI的中斷向量(vector=02h)發送給CPU,CPU就會根據vector在中斷向量表(interrupt table)中去尋找對應的NMI處理程序。在source  code中找到這么一段代碼:

PUBLIC CsmOemInterrupts

PUBLIC CsmOemInterruptsEnd

CsmOemInterrupts LABEL WORD

         mBODY_ID_AND_TBL_CSM_ENTRY_NEAR 00002h, OEMINT2

mEND_TBL_CSM CsmOemInterrupts

mBODY_ID_AND_TBL_CSM_ENTRY_NEAR和mEND_TBL_CSM都是宏定義,它們表示在memory中分配了一段interrupt table的地址空間,范圍是0000h——FFFFh。OEMINT2是處理NMI中斷的proc,被定義在interrupt table中的02號位置處。這段code就表明了NMI的中斷處理程序是OEMINT2。OEMINT2通過ELINK在CsmOemInterrupts執行后被調用,CsmOemInterrupts本身也是通過ELINK被調用,但不清楚何時被調用,source code 里面定義的調用順序是InvokeOrder = TableFunction,找不到相關的解釋,不知是否可理解成在CPU接到中斷請求,准備查找中斷向量表時調用。

 

 

 

硬件觸發NMI的流程

1、  將對應的GPIO(GPIO9)設置成GPI

2、  將GPIO9的寄存器GPI_INV設置成0(上升沿觸發)或1(下降沿觸發)

3、  將GPI_ROUT寄存器routed到可觸發GPIO9的NMI的功能

4、  將GPI_NMI_EN寄存器中GPIO9匹配的位置1

5、  將GPI_NMI_STS寄存器中GPIO9匹配的位寫1清零

 

CPU怎樣知道SMI被觸發?

以產生GPIO14上的SMI為例。在DXE階段,會為SMI做好准備,主要是在InSmmFunction()做兩個動作,一是pBS->LocateProtocol(),二是pGpiDispatch->Register(),它的函數原形是EfiSmmSwRegister(),其作用就是注冊一個SMI,具體過程是通過增加一個鏈表的節點來實現。注冊好的SMI節點會存放在SMRAM這段地址空間中(30000h+8000h),SMRAM這段空間在非SMI模式下時當做常規內存使用,在SMI模式下,CPU就會在SMRAM這段空間中取得SMI的資源。注冊了以后,CPU是怎樣知道是GPIO14產生的SMI呢?當GPIO14的SMI被觸發后,系統會進入SMM模式。在SMM模式中,CPU就會去尋找時哪個GPI產生的SMI。InstallChildDispatchHandler這個Driver的入口函數中,會調用InitSmmHandler()這個函數,這個函數中又會調用pSmmBase->RegisterCallback(pSmmBase, ImageHandle, ChildDispatcher, FALSE, FALSE),其中參數ChildDispatcher也是函數,CPU會通過ChildDispatcher()里的GetContextGpi()來判斷是否有GPI產生了SMI中斷,如有SMI中斷的話,就將其中的gSmiContext.GpiSource 設置為TRUE,在ChildDispatcher()的另外一個地方調用if (gSmiContext.GpiSource)作判斷,如為真,則執行EfiSmmGpiDispatch(&gSmiContext.GpiContext),在這個函數中有這么兩條語句                                       if (Link->Context.GpiNum & Context->GpiNum)

            Link->Function(Link, &Link->Context);

Link->Function()在注冊的時候EfiSmmSwRegister()中賦值,其值就是設置觸發NMI相關寄存器的SBGpi14SmiHandler()函數,經過這個過程以后,NMI就會被觸發。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM