前面介绍了 IDT ,这里继续研究Shell 下是如何处理 Exception的。
同样,前面提到过,Shell下当一个中断发生之后会从 IDT 中查找Vector入口,然后跳转进去执行。
1.代码在\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ExceptionHandlerAsm.nasm 可以看作用是在尽量短的代码中跳转到对应的处理代码
AsmIdtVectorBegin:
%rep 32
db 0x6a ; push #VectorNum
db ($ - AsmIdtVectorBegin) / ((AsmIdtVectorEnd - AsmIdtVectorBegin) / 32) ; VectorNum
push rax
mov rax, strict qword 0 ; mov rax, ASM_PFX(CommonInterruptEntry)
jmp rax
%endrep
AsmIdtVectorEnd:
2.同一个文件中CommonInterruptEntry 函数,在堆栈中有当前的 Vector Number
;---------------------------------------;
; CommonInterruptEntry ;
;---------------------------------------;
; The follow algorithm is used for the common interrupt routine.
; Entry from each interrupt with a push eax and eax=interrupt number
; Stack frame would be as follows as specified in IA32 manuals:
;
; +---------------------+ <-- 16-byte aligned ensured by processor
; + Old SS +
; +---------------------+
; + Old RSP +
; +---------------------+
; + RFlags +
; +---------------------+
; + CS +
; +---------------------+
; + RIP +
; +---------------------+
; + Error Code +
; +---------------------+
; + Vector Number +
; +---------------------+
; + RBP +
; +---------------------+ <-- RBP, 16-byte aligned
; The follow algorithm is used for the common interrupt routine.
global ASM_PFX(CommonInterruptEntry)
ASM_PFX(CommonInterruptEntry):
cli
pop rax
;
; All interrupt handlers are invoked through interrupt gates, so
; IF flag automatically cleared at the entry point
;
xchg rcx, [rsp] ; Save rcx into stack and save vector number into rcx
and rcx, 0xFF
cmp ecx, 32 ; Intel reserved vector for exceptions?
jae NoErrorCode
bt [ASM_PFX(mErrorCodeFlag)], ecx
jc HasErrorCode
……省略…….
;
; Per X64 calling convention, allocate maximum parameter stack space
; and make sure RSP is 16-byte aligned
;
sub rsp, 4 * 8 + 8
call ASM_PFX(CommonExceptionHandler)
add rsp, 4 * 8 + 8
……省略…….
3.对于 Shell 下,会跳转到\UefiCpuPkg\Library\CpuExceptionHandlerLib\DxeException.c 这个文件中。 所有的 Shell 下中断都会经过这里。
/**
Common exception handler.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
CommonExceptionHandler (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
CommonExceptionHandlerWorker (ExceptionType, SystemContext, &mExceptionHandlerData);
}
4.真正工作的是 \UefiCpuPkg\Library\CpuExceptionHandlerLib\PeiDxeSmmCpuException.c
/**
Internal worker function for common exception handler.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
@param ExceptionHandlerData Pointer to exception handler data.
**/
VOID
CommonExceptionHandlerWorker (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext,
IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
)
{
EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext;
RESERVED_VECTORS_DATA *ReservedVectors;
EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler;
ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32);
ReservedVectors = ExceptionHandlerData->ReservedVectors;
ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
……省略……
这里,可以使用 DEBUG宏输出调试信息,可以用串口输出看到完整的信息。
上面的流程使用 DCI 确认过,有兴趣的朋友可以同样尝试使用 DCI来观察。需要注意的是,有时候调用无法使用单步跟踪指令完成,推荐使用在要跳转到的位置下断点的方式来进行追踪。
此外,IBV 的代码可能会对文件进行 Override,就是同一个代码中出现两个同样名称的文件,两者大部分代码相同,但是会存在一些细节差别,这在追踪过程中需要特别注意。