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就会被触发。

posted on 2018-03-13 14:31  米兰达莫西  阅读(10913)  评论(0编辑  收藏  举报