;------------------------------------------------------------------------------\r
%include "Nasm.inc"\r
\r
+;\r
+; Equivalent NASM structure of IA32_DESCRIPTOR\r
+;\r
+struc IA32_DESCRIPTOR\r
+ .Limit CTYPE_UINT16 1\r
+ .Base CTYPE_UINTN 1\r
+endstruc\r
+\r
+;\r
+; Equivalent NASM structure of IA32_IDT_GATE_DESCRIPTOR\r
+;\r
+struc IA32_IDT_GATE_DESCRIPTOR\r
+ .OffsetLow CTYPE_UINT16 1\r
+ .Selector CTYPE_UINT16 1\r
+ .Reserved_0 CTYPE_UINT8 1\r
+ .GateType CTYPE_UINT8 1\r
+ .OffsetHigh CTYPE_UINT16 1\r
+ .OffsetUpper CTYPE_UINT32 1\r
+ .Reserved_1 CTYPE_UINT32 1\r
+endstruc\r
+\r
;\r
; CommonExceptionHandler()\r
;\r
\r
%define VC_EXCEPTION 29\r
-%define PF_EXCEPTION 14\r
\r
extern ASM_PFX(mErrorCodeFlag) ; Error code flags for exceptions\r
extern ASM_PFX(mDoFarReturnFlag) ; Do far return flag\r
extern ASM_PFX(CommonExceptionHandler)\r
-extern ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard))\r
\r
SECTION .data\r
\r
\r
; The follow algorithm is used for clear shadow stack token busy bit.\r
; The comment is based on the sample shadow stack.\r
+ ; Shadow stack is 32 bytes aligned.\r
; The sample shadow stack layout :\r
; Address | Context\r
; +-------------------------+\r
- ; 0xFD0 | FREE | it is 0xFD8|0x02|(LMA & CS.L), after SAVEPREVSSP.\r
+ ; 0xFB8 | FREE | It is 0xFC0|0x02|(LMA & CS.L), after SAVEPREVSSP.\r
; +-------------------------+\r
- ; 0xFD8 | Prev SSP |\r
+ ; 0xFC0 | Prev SSP |\r
; +-------------------------+\r
- ; 0xFE0 | RIP |\r
+ ; 0xFC8 | RIP |\r
; +-------------------------+\r
- ; 0xFE8 | CS |\r
+ ; 0xFD0 | CS |\r
; +-------------------------+\r
- ; 0xFF0 | 0xFF0 | BUSY | BUSY flag cleared after CLRSSBSY\r
+ ; 0xFD8 | 0xFD8 | BUSY | BUSY flag cleared after CLRSSBSY\r
; +-------------------------+\r
- ; 0xFF8 | 0xFD8|0x02|(LMA & CS.L) |\r
+ ; 0xFE0 | 0xFC0|0x02|(LMA & CS.L) |\r
; +-------------------------+\r
; Instructions for Intel Control Flow Enforcement Technology (CET) are supported since NASM version 2.15.01.\r
cmp qword [ASM_PFX(mDoFarReturnFlag)], 0\r
jz CetDone\r
- cmp qword [rbp + 8], PF_EXCEPTION ; check if it is a Page Fault\r
- jnz CetDone\r
- cmp byte [dword ASM_PFX(FeaturePcdGet (PcdCpuSmmStackGuard))], 0\r
- jz CetDone\r
mov rax, cr4\r
- and rax, 0x800000 ; check if CET is enabled\r
+ and rax, 0x800000 ; Check if CET is enabled\r
+ jz CetDone\r
+ sub rsp, 0x10\r
+ sidt [rsp]\r
+ mov rcx, qword [rsp + IA32_DESCRIPTOR.Base]; Get IDT base address\r
+ add rsp, 0x10\r
+ mov rax, qword [rbp + 8]; Get exception number\r
+ sal rax, 0x04 ; Get IDT offset\r
+ add rax, rcx ; Get IDT gate descriptor address\r
+ mov al, byte [rax + IA32_IDT_GATE_DESCRIPTOR.Reserved_0]\r
+ and rax, 0x01 ; Check IST field\r
jz CetDone\r
- ; SSP should be 0xFD8 at this point\r
+ ; SSP should be 0xFC0 at this point\r
mov rax, 0x04 ; advance past cs:lip:prevssp;supervisor shadow stack token\r
- INCSSP_RAX ; After this SSP should be 0xFF8\r
- SAVEPREVSSP ; now the shadow stack restore token will be created at 0xFD0\r
- READSSP_RAX ; Read new SSP, SSP should be 0x1000\r
+ INCSSP_RAX ; After this SSP should be 0xFE0\r
+ SAVEPREVSSP ; now the shadow stack restore token will be created at 0xFB8\r
+ READSSP_RAX ; Read new SSP, SSP should be 0xFE8\r
sub rax, 0x10\r
- CLRSSBSY_RAX ; Clear token at 0xFF0, SSP should be 0 after this\r
+ CLRSSBSY_RAX ; Clear token at 0xFD8, SSP should be 0 after this\r
sub rax, 0x20\r
- RSTORSSP_RAX ; Restore to token at 0xFD0, new SSP will be 0xFD0\r
+ RSTORSSP_RAX ; Restore to token at 0xFB8, new SSP will be 0xFB8\r
mov rax, 0x01 ; Pop off the new save token created\r
- INCSSP_RAX ; SSP should be 0xFD8 now\r
+ INCSSP_RAX ; SSP should be 0xFC0 now\r
CetDone:\r
\r
cli\r
mSmmStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStackSize)));\r
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
//\r
- // 2 more pages is allocated for each processor.\r
- // one is guard page and the other is known good stack.\r
+ // SMM Stack Guard Enabled\r
+ // 2 more pages is allocated for each processor, one is guard page and the other is known good stack.\r
//\r
- // +-------------------------------------------+-----+-------------------------------------------+\r
- // | Known Good Stack | Guard Page | SMM Stack | ... | Known Good Stack | Guard Page | SMM Stack |\r
- // +-------------------------------------------+-----+-------------------------------------------+\r
- // | | | |\r
- // |<-------------- Processor 0 -------------->| |<-------------- Processor n -------------->|\r
+ // +--------------------------------------------------+-----+--------------------------------------------------+\r
+ // | Known Good Stack | Guard Page | SMM Stack | ... | Known Good Stack | Guard Page | SMM Stack |\r
+ // +--------------------------------------------------+-----+--------------------------------------------------+\r
+ // | 4K | 4K PcdCpuSmmStackSize| | 4K | 4K PcdCpuSmmStackSize|\r
+ // |<---------------- mSmmStackSize ----------------->| |<---------------- mSmmStackSize ----------------->|\r
+ // | | | |\r
+ // |<------------------ Processor 0 ----------------->| |<------------------ Processor n ----------------->|\r
//\r
mSmmStackSize += EFI_PAGES_TO_SIZE (2);\r
}\r
\r
mSmmShadowStackSize = 0;\r
if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {\r
- //\r
- // Append Shadow Stack after normal stack\r
- //\r
- // |= Stacks\r
- // +--------------------------------------------------+---------------------------------------------------------------+\r
- // | Known Good Stack | Guard Page | SMM Stack | Known Good Shadow Stack | Guard Page | SMM Shadow Stack |\r
- // +--------------------------------------------------+---------------------------------------------------------------+\r
- // | |PcdCpuSmmStackSize| |PcdCpuSmmShadowStackSize|\r
- // |<---------------- mSmmStackSize ----------------->|<--------------------- mSmmShadowStackSize ------------------->|\r
- // | |\r
- // |<-------------------------------------------- Processor N ------------------------------------------------------->|\r
- //\r
mSmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize)));\r
+\r
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
+ //\r
+ // SMM Stack Guard Enabled\r
+ // Append Shadow Stack after normal stack\r
+ // 2 more pages is allocated for each processor, one is guard page and the other is known good shadow stack.\r
+ //\r
+ // |= Stacks\r
+ // +--------------------------------------------------+---------------------------------------------------------------+\r
+ // | Known Good Stack | Guard Page | SMM Stack | Known Good Shadow Stack | Guard Page | SMM Shadow Stack |\r
+ // +--------------------------------------------------+---------------------------------------------------------------+\r
+ // | 4K | 4K |PcdCpuSmmStackSize| 4K | 4K |PcdCpuSmmShadowStackSize|\r
+ // |<---------------- mSmmStackSize ----------------->|<--------------------- mSmmShadowStackSize ------------------->|\r
+ // | |\r
+ // |<-------------------------------------------- Processor N ------------------------------------------------------->|\r
+ //\r
mSmmShadowStackSize += EFI_PAGES_TO_SIZE (2);\r
+ } else {\r
+ //\r
+ // SMM Stack Guard Disabled (Known Good Stack is still required for potential stack switch.)\r
+ // Append Shadow Stack after normal stack with 1 more page as known good shadow stack.\r
+ // 1 more pages is allocated for each processor, it is known good stack.\r
+ //\r
+ //\r
+ // |= Stacks\r
+ // +-------------------------------------+--------------------------------------------------+\r
+ // | Known Good Stack | SMM Stack | Known Good Shadow Stack | SMM Shadow Stack |\r
+ // +-------------------------------------+--------------------------------------------------+\r
+ // | 4K |PcdCpuSmmStackSize| 4K |PcdCpuSmmShadowStackSize|\r
+ // |<---------- mSmmStackSize ---------->|<--------------- mSmmShadowStackSize ------------>|\r
+ // | |\r
+ // |<-------------------------------- Processor N ----------------------------------------->|\r
+ //\r
+ mSmmShadowStackSize += EFI_PAGES_TO_SIZE (1);\r
+ mSmmStackSize += EFI_PAGES_TO_SIZE (1);\r
}\r
}\r
\r
VOID\r
);\r
\r
+/**\r
+ Initialize IDT IST Field.\r
+\r
+ @param[in] ExceptionType Exception type.\r
+ @param[in] Ist IST value.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+InitializeIdtIst (\r
+ IN EFI_EXCEPTION_TYPE ExceptionType,\r
+ IN UINT8 Ist\r
+ );\r
+\r
/**\r
Initialize Gdt for all processors.\r
\r
// Additional SMM IDT initialization for SMM stack guard\r
//\r
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
- InitializeIDTSmmStackGuard ();\r
+ DEBUG ((DEBUG_INFO, "Initialize IDT IST field for SMM Stack Guard\n"));\r
+ InitializeIdtIst (EXCEPT_IA32_PAGE_FAULT, 1);\r
+ }\r
+\r
+ //\r
+ // Additional SMM IDT initialization for SMM CET shadow stack\r
+ //\r
+ if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {\r
+ DEBUG ((DEBUG_INFO, "Initialize IDT IST field for SMM Shadow Stack\n"));\r
+ InitializeIdtIst (EXCEPT_IA32_PAGE_FAULT, 1);\r
+ InitializeIdtIst (EXCEPT_IA32_MACHINE_CHECK, 1);\r
}\r
\r
//\r
UINTN mSmmInterruptSspTables;\r
\r
/**\r
- Initialize IDT for SMM Stack Guard.\r
+ Initialize IDT IST Field.\r
+\r
+ @param[in] ExceptionType Exception type.\r
+ @param[in] Ist IST value.\r
\r
**/\r
VOID\r
EFIAPI\r
-InitializeIDTSmmStackGuard (\r
- VOID\r
+InitializeIdtIst (\r
+ IN EFI_EXCEPTION_TYPE ExceptionType,\r
+ IN UINT8 Ist\r
)\r
{\r
IA32_IDT_GATE_DESCRIPTOR *IdtGate;\r
\r
- //\r
- // If SMM Stack Guard feature is enabled, set the IST field of\r
- // the interrupt gate for Page Fault Exception to be 1\r
- //\r
IdtGate = (IA32_IDT_GATE_DESCRIPTOR *)gcSmiIdtr.Base;\r
- IdtGate += EXCEPT_IA32_PAGE_FAULT;\r
- IdtGate->Bits.Reserved_0 = 1;\r
+ IdtGate += ExceptionType;\r
+ IdtGate->Bits.Reserved_0 = Ist;\r
}\r
\r
/**\r
GdtDescriptor->Bits.BaseMid = (UINT8)((UINTN)TssBase >> 16);\r
GdtDescriptor->Bits.BaseHigh = (UINT8)((UINTN)TssBase >> 24);\r
\r
- if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
+ if ((FeaturePcdGet (PcdCpuSmmStackGuard)) || ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported)) {\r
//\r
// Setup top of known good stack as IST1 for each processor.\r
//\r
\r
if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {\r
SmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize)));\r
+ //\r
+ // Add 1 page as known good shadow stack\r
+ //\r
+ SmmShadowStackSize += EFI_PAGES_TO_SIZE (1);\r
+\r
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
- SmmShadowStackSize += EFI_PAGES_TO_SIZE (2);\r
+ //\r
+ // Add one guard page between Known Good Shadow Stack and SMM Shadow Stack.\r
+ //\r
+ SmmShadowStackSize += EFI_PAGES_TO_SIZE (1);\r
}\r
mCetPl0Ssp = (UINT32)((UINTN)ShadowStack + SmmShadowStackSize - sizeof(UINT64));\r
PatchInstructionX86 (mPatchCetPl0Ssp, mCetPl0Ssp, 4);\r
DEBUG ((DEBUG_INFO, "ShadowStack - 0x%x\n", ShadowStack));\r
DEBUG ((DEBUG_INFO, " SmmShadowStackSize - 0x%x\n", SmmShadowStackSize));\r
\r
- if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
- if (mSmmInterruptSspTables == 0) {\r
- mSmmInterruptSspTables = (UINTN)AllocateZeroPool(sizeof(UINT64) * 8 * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus);\r
- ASSERT (mSmmInterruptSspTables != 0);\r
- DEBUG ((DEBUG_INFO, "mSmmInterruptSspTables - 0x%x\n", mSmmInterruptSspTables));\r
- }\r
-\r
- //\r
- // The highest address on the stack (0xFF8) is a save-previous-ssp token pointing to a location that is 40 bytes away - 0xFD0.\r
- // The supervisor shadow stack token is just above it at address 0xFF0. This is where the interrupt SSP table points.\r
- // So when an interrupt of exception occurs, we can use SAVESSP/RESTORESSP/CLEARSSBUSY for the supervisor shadow stack,\r
- // due to the reason the RETF in SMM exception handler cannot clear the BUSY flag with same CPL.\r
- // (only IRET or RETF with different CPL can clear BUSY flag)\r
- // Please refer to UefiCpuPkg/Library/CpuExceptionHandlerLib/X64 for the full stack frame at runtime.\r
- //\r
- InterruptSsp = (UINT32)((UINTN)ShadowStack + EFI_PAGES_TO_SIZE(1) - sizeof(UINT64));\r
- *(UINT64 *)(UINTN)InterruptSsp = (InterruptSsp - sizeof(UINT64) * 4) | 0x2;\r
- mCetInterruptSsp = InterruptSsp - sizeof(UINT64);\r
-\r
- mCetInterruptSspTable = (UINT32)(UINTN)(mSmmInterruptSspTables + sizeof(UINT64) * 8 * CpuIndex);\r
- InterruptSspTable = (UINT64 *)(UINTN)mCetInterruptSspTable;\r
- InterruptSspTable[1] = mCetInterruptSsp;\r
- PatchInstructionX86 (mPatchCetInterruptSsp, mCetInterruptSsp, 4);\r
- PatchInstructionX86 (mPatchCetInterruptSspTable, mCetInterruptSspTable, 4);\r
- DEBUG ((DEBUG_INFO, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp));\r
- DEBUG ((DEBUG_INFO, "mCetInterruptSspTable - 0x%x\n", mCetInterruptSspTable));\r
+ if (mSmmInterruptSspTables == 0) {\r
+ mSmmInterruptSspTables = (UINTN)AllocateZeroPool(sizeof(UINT64) * 8 * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus);\r
+ ASSERT (mSmmInterruptSspTables != 0);\r
+ DEBUG ((DEBUG_INFO, "mSmmInterruptSspTables - 0x%x\n", mSmmInterruptSspTables));\r
}\r
+\r
+ //\r
+ // The highest address on the stack (0xFE0) is a save-previous-ssp token pointing to a location that is 40 bytes away - 0xFB8.\r
+ // The supervisor shadow stack token is just above it at address 0xFD8. This is where the interrupt SSP table points.\r
+ // So when an interrupt of exception occurs, we can use SAVESSP/RESTORESSP/CLEARSSBUSY for the supervisor shadow stack,\r
+ // due to the reason the RETF in SMM exception handler cannot clear the BUSY flag with same CPL.\r
+ // (only IRET or RETF with different CPL can clear BUSY flag)\r
+ // Please refer to UefiCpuPkg/Library/CpuExceptionHandlerLib/X64 for the full stack frame at runtime.\r
+ // According to SDM (ver. 075 June 2021), shadow stack should be 32 bytes aligned.\r
+ //\r
+ InterruptSsp = (UINT32)(((UINTN)ShadowStack + EFI_PAGES_TO_SIZE(1) - (sizeof(UINT64) * 4)) & ~0x1f);\r
+ *(UINT64 *)(UINTN)InterruptSsp = (InterruptSsp - sizeof(UINT64) * 4) | 0x2;\r
+ mCetInterruptSsp = InterruptSsp - sizeof(UINT64);\r
+\r
+ mCetInterruptSspTable = (UINT32)(UINTN)(mSmmInterruptSspTables + sizeof(UINT64) * 8 * CpuIndex);\r
+ InterruptSspTable = (UINT64 *)(UINTN)mCetInterruptSspTable;\r
+ InterruptSspTable[1] = mCetInterruptSsp;\r
+ PatchInstructionX86 (mPatchCetInterruptSsp, mCetInterruptSsp, 4);\r
+ PatchInstructionX86 (mPatchCetInterruptSspTable, mCetInterruptSspTable, 4);\r
+ DEBUG ((DEBUG_INFO, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp));\r
+ DEBUG ((DEBUG_INFO, "mCetInterruptSspTable - 0x%x\n", mCetInterruptSspTable));\r
}\r
}\r
\r