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