文章目录
- 一、SMI的介绍
- 二、SMI的作用
-
- SMI作用
- 如何触发SMI
- 三、SMM状态的进入和退出
-
- 1.SMM状态的进入和退出
- 2.CPU的其他状态和SMM状态之间的转换
- 四、SMI执行程序处理过程
- 五、SMI action in Bios
- 六、SMI相关的实际EDKII代码
一、SMI的介绍
Sytem Management Interrupt,系统管理中断。 打个比方,工作的时候,突然打来一个电话,这个时候你停下手中所有的事去接电话,电话打完了再回来继续工作,打电话这个过程就相当于SMI。
关于SMI和SMM(system management mode): SMI发出后,CPU进入SMM状态。 SMM是intel引入x86的一种CPU的执行模式,只能通过SMI进入,并只能通过执行RSM指令退出。 SMM driver,SMM模式下的执行的程序,由bios实现,code里会写入。
SMRAM:Sytem Management RAM。 内存中的一段区域,所有的SMM driver在SMRAM中运行,非SMM下SMRAM不可访问。
二、SMI的作用
SMI作用
比如某些刷写bios的工具使用SMI实现刷写工作。CPU进入SMM模式后,鼠标和键盘都是不能动的,此时其他的中断都不可切断进程,直到退出SMM。就算再来一个SMI也要等这个SMI处理完退出SMM、再触发SMI进入SMM。
比如os下输入sleep number=3的睡眠模式,会触发SMI去做一些电源管理的功能:内存不断电。
如何触发SMI
进入SMM的唯一途径是产生SMI信号。 这个信号会通过CPU的SMI pin脚产生,或者CPU从APIC bus上收到SMI message。
硬件触发 系统event error,当有些硬件error产生的时候触发SMI。 RTC alarm (real time clock实时始终警告) GPIO触发 IO trap,读写某些IO端口时触发SMI(可以在代码里设定)。 其它等,例如电源按钮。
软件触发 写任意数据到IO port 0XB2或者BIOS中定义的其他port。
三、SMM状态的进入和退出
1.SMM状态的进入和退出
- SMM状态的进入和退出过程
和interrupt的处理类似,但有些不同,SMI产生后,在进入SMM的时候,CPU保存了中断程序的上下文(状态、数据)到SMRAM中。 CPU跳转到SMRAM SmiHandler,执行完毕后,rsm指令执行,CPU restore context,原来的程序继续执行。
- SMM状态的特点
SMM状态下所有中断和SMI被屏蔽,SMI处理程序不可重入(还没执行完之前不能跳去执行其他的SMI)。 SMI的优先级比exception和中断的优先级高。如果NMI、可屏蔽硬件中断、调试指令和SMI同时向CPU发送信号,只有SMI会被处理。
0x40-0xFE是逻辑设备的配置,分配资源和进行管理。
2.CPU的其他状态和SMM状态之间的转换
SMI信号产生后,不管CPU是实模式、保护模式还是虚拟8086模式,都会导致CPU切换到SMM状态。 执行指令RSM后,CPU回到进入SMM模式之前的那个模式。
四、SMI执行程序处理过程
内存地址空间中的某两段:CPU正在执行的程序、SMRAM。 SMI产生后,CPU从当前地址跳出,立马进入到SMRAM里去,入口地址为:SMRAM起始地址(SMBase)+8000H。 SMI的程序执行完之后,CPU再通过rsm指令退出SMM,回到原先的内存地址中去。
- SMRAM usage
当CPU在SMM里的时候,cpu执行代码实在SMRAM空间里,SMBase默认地址在30000H。 较早的bios,post过程中SMBase被设定为位于A000:0或B000:0,这个区域原本用于decode到video buffer地址,进SMI的时候,硬件会将这个地址切换到真实的内存区域,现在BIOS一般将SMBase设定为在Tseg区域,由CPU或北桥MCH控制。 SMRAM空间映射到CPU的物理地址空间,CPU使用这个空间来保存进入SMI之前CPU的上下文和存储SMI的处理程序代码、数据和堆栈。它也可以用来存储SMmessage(比如系统配置和关闭设备的具体信息)等。
五、SMI action in Bios
- SMRAM usage
由Bios实现。 寻址空间0~4G。 无特权级别,类似big real mode,不存在任何权限级别或地址映射。 进SMM后,CPU的通用寄存器的值不确定,其它段寄存器等被初始化为特定值。 可以执行所有指令比如IO读写,但是CPU指令只在SMRAM内执行。
- Bios中SMI程序作用
flash操作,读、写、擦除,获取flash信息。 get/set smbios information。 USB driver的实现。 kbc emulation,键盘的模拟。 实现某些类似timer的功能,比如RTC。 error logging。 其它等,比如电源管理的s3、s4的实现调用smi。
- SMI程序作用,举例
六、SMI相关的实际EDKII代码
UEFI规定了归于SMI的EFI_SMM_BASE2_PROTOCOL:
struct _EFI_SMM_BASE2_PROTOCOL {
EFI_SMM_INSIDE_OUT2 InSmm;
EFI_SMM_GET_SMST_LOCATION2 GetSmstLocation;
};
InSmm:这个server提供的是这个driver是否运行在SMM初始化的阶段。是的话返回成功,失败的话返回NULL。
GetSmstLocation:这个函数是locate到system management service table(SMST),SMST有单独的smmallocatepool、smminstallprotocol、smmlocateprotocol函数等,SMM不能调用之前外部的protocol。
- SMM child dispatch protocol
SMM触发代码,在inf类型为DXE_SMM_DRIVER里的c文件里执行。
代码里先调用InSmm确保当前处在smm init阶段,如果是的话才往下执行。
EFI_STATUS Status;
BOOLEAN InSmm;
EFI_SMM_BASE2_PROTOCOL *SmmBase2;
EFI_SMM_SYSTEM_TABLE2 *mBootScriptSmst = NULL;
gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&SmmBase2);
Status = SmmBase2->InSmm (SmmBase2, &InSmm);
if (EFI_ERROR (Status)|(!InSmm)) {
return RETURN_SUCCESS;
}
Status = SmmBase2->GetSmstLocation (SmmBase2, &mBootScriptSmst);
e.g.要用EFI_SMM_SW_DISPATCH2_PROTOCOL进行软件触发的注册,对应的inf的protocol里包含:
[Protocols]
gEfiSmmSwDispatch2ProtocolGuid
//我自己定义成9,触发smi(软件触发,写Sample_number到B2)后写8到80口
#define Sample_number 9
EFI_STATUS
EFIAPI
MyCallback(
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *Context,
IN OUT VOID *CommBuffer,
IN OUT UINTN *CommBufferSize
)
{
IoWrite8(0x80,Sample_number);
return EFI_SUCCESS;
}
EFI_STATUS
SwSample (
VOID
)
{
EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
EFI_SMM_SW_REGISTER_CONTEXT SwContext;
EFI_HANDLE SwHandle;
EFI_STATUS Status;
Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID **)&SwDispatch);
ASSERT_EFI_ERROR (Status);
SwContext.SwSmiInputValue = Sample_number; //具体数值自己定义,这是要写到B2口的数值
Status = SwDispatch->Register (SwDispatch, MyCallback, &SwContext, &SwHandle); //MyCallback是调用的函数
e.g.要用EFI_SMM_SX_DISPATCH2_PROTOCOL进行sleep触发,对应的inf的protocol里包含:
[Protocols]
gEfiSmmSxDispatch2ProtocolGuid
EFI_STATUS
EFIAPI
SmmS3EntryCallBack(
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *Context OPTIONAL,
IN OUT VOID *CommBuffer OPTIONAL,
IN OUT UINTN *CommBufferSize OPTIONAL
)
{
mDuringS3Resume = TRUE; //自己写
return EFI_SUCCESS;
}
EFI_STATUS
SxSample (
VOID
)
{
EFI_SMM_SX_DISPATCH2_PROTOCOL *SxDispatch;
EFI_SMM_SX_REGISTER_CONTEXT EntryRegisterContext;
EFI_HANDLE S3EntryHandle;
EFI_STATUS Status;
Status = gSmst->SmmLocateProtocol (
&gEfiSmmSxDispatch2ProtocolGuid,
NULL,
(VOID **)&SxDispatch //一会儿要用来register的
);
ASSERT_EFI_ERROR (Status);
EntryRegisterContext.Type = SxS3; //S3阶段
EntryRegisterContext.Phase = SxEntry; //进入的时候
Status = SxDispatch->Register (
SxDispatch,
SmmS3EntryCallBack, //我们定义的callback函数
&EntryRegisterContext,
&S3EntryHandle
);
评论前必须登录!
注册