; Notes:\r
;\r
;------------------------------------------------------------------------------\r
+%include "Nasm.inc"\r
\r
;\r
; CommonExceptionHandler()\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
push qword [rax + 0x18] ; save EFLAGS in new location\r
mov rax, [rax] ; restore rax\r
popfq ; restore EFLAGS\r
- DB 0x48 ; prefix to composite "retq" with next "retf"\r
- retf ; far return\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
+ ; The sample shadow stack layout :\r
+ ; Address | Context\r
+ ; +-------------------------+\r
+ ; 0xFD0 | FREE | it is 0xFD8|0x02|(LMA & CS.L), after SAVEPREVSSP.\r
+ ; +-------------------------+\r
+ ; 0xFD8 | Prev SSP |\r
+ ; +-------------------------+\r
+ ; 0xFE0 | RIP |\r
+ ; +-------------------------+\r
+ ; 0xFE8 | CS |\r
+ ; +-------------------------+\r
+ ; 0xFF0 | 0xFF0 | BUSY | BUSY flag cleared after CLRSSBSY\r
+ ; +-------------------------+\r
+ ; 0xFF8 | 0xFD8|0x02|(LMA & CS.L) |\r
+ ; +-------------------------+\r
+ ; Instructions for Intel Control Flow Enforcement Technology (CET) are supported since NASM version 2.15.01.\r
+ push rax ; SSP should be 0xFD8 at this point\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
+ jz CetDone\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
+ push rax\r
+ sub rax, 0x10\r
+ CLRSSBSY_RAX ; Clear token at 0xFF0, SSP should be 0 after this\r
+ sub rax, 0x20\r
+ RSTORSSP_RAX ; Restore to token at 0xFD0, new SSP will be 0xFD0\r
+ pop rax\r
+ mov rax, 0x01 ; Pop off the new save token created\r
+ INCSSP_RAX ; SSP should be 0xFD8 now\r
+CetDone:\r
+ pop rax ; restore rax\r
+\r
+ DB 0x48 ; prefix to composite "retq" with next "retf"\r
+ retf ; far return\r
DoIret:\r
iretq\r
\r
{\r
UINTN SmmShadowStackSize;\r
UINT64 *InterruptSspTable;\r
+ UINT32 InterruptSsp;\r
\r
if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {\r
SmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize)));\r
ASSERT (mSmmInterruptSspTables != 0);\r
DEBUG ((DEBUG_INFO, "mSmmInterruptSspTables - 0x%x\n", mSmmInterruptSspTables));\r
}\r
- mCetInterruptSsp = (UINT32)((UINTN)ShadowStack + EFI_PAGES_TO_SIZE(1) - sizeof(UINT64));\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
+ *(UINT32 *)(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