2 SMM CPU misc functions for x64 arch specific.
4 Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include "PiSmmCpuDxeSmm.h"
11 EFI_PHYSICAL_ADDRESS mGdtBuffer
;
14 extern BOOLEAN mCetSupported
;
15 extern UINTN mSmmShadowStackSize
;
17 X86_ASSEMBLY_PATCH_LABEL mPatchCetPl0Ssp
;
18 X86_ASSEMBLY_PATCH_LABEL mPatchCetInterruptSsp
;
19 X86_ASSEMBLY_PATCH_LABEL mPatchCetInterruptSspTable
;
21 UINT32 mCetInterruptSsp
;
22 UINT32 mCetInterruptSspTable
;
24 UINTN mSmmInterruptSspTables
;
27 Initialize IDT IST Field.
29 @param[in] ExceptionType Exception type.
30 @param[in] Ist IST value.
36 IN EFI_EXCEPTION_TYPE ExceptionType
,
40 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
42 IdtGate
= (IA32_IDT_GATE_DESCRIPTOR
*)gcSmiIdtr
.Base
;
43 IdtGate
+= ExceptionType
;
44 IdtGate
->Bits
.Reserved_0
= Ist
;
48 Initialize Gdt for all processors.
50 @param[in] Cr3 CR3 value.
51 @param[out] GdtStepSize The step size for GDT table.
53 @return GdtBase for processor 0.
54 GdtBase for processor X is: GdtBase + (GdtStepSize * X)
59 OUT UINTN
*GdtStepSize
63 IA32_SEGMENT_DESCRIPTOR
*GdtDescriptor
;
65 UINTN GdtTssTableSize
;
67 UINTN GdtTableStepSize
;
70 // For X64 SMM, we allocate separate GDT/TSS for each CPUs to avoid TSS load contention
73 GdtTssTableSize
= (gcSmiGdtr
.Limit
+ 1 + TSS_SIZE
+ 7) & ~7; // 8 bytes aligned
74 mGdtBufferSize
= GdtTssTableSize
* gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
;
75 GdtTssTables
= (UINT8
*)AllocateCodePages (EFI_SIZE_TO_PAGES (mGdtBufferSize
));
76 ASSERT (GdtTssTables
!= NULL
);
77 mGdtBuffer
= (UINTN
)GdtTssTables
;
78 GdtTableStepSize
= GdtTssTableSize
;
80 for (Index
= 0; Index
< gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
; Index
++) {
81 CopyMem (GdtTssTables
+ GdtTableStepSize
* Index
, (VOID
*)(UINTN
)gcSmiGdtr
.Base
, gcSmiGdtr
.Limit
+ 1 + TSS_SIZE
);
84 // Fixup TSS descriptors
86 TssBase
= (UINTN
)(GdtTssTables
+ GdtTableStepSize
* Index
+ gcSmiGdtr
.Limit
+ 1);
87 GdtDescriptor
= (IA32_SEGMENT_DESCRIPTOR
*)(TssBase
) - 2;
88 GdtDescriptor
->Bits
.BaseLow
= (UINT16
)(UINTN
)TssBase
;
89 GdtDescriptor
->Bits
.BaseMid
= (UINT8
)((UINTN
)TssBase
>> 16);
90 GdtDescriptor
->Bits
.BaseHigh
= (UINT8
)((UINTN
)TssBase
>> 24);
92 if ((FeaturePcdGet (PcdCpuSmmStackGuard
)) || ((PcdGet32 (PcdControlFlowEnforcementPropertyMask
) != 0) && mCetSupported
)) {
94 // Setup top of known good stack as IST1 for each processor.
96 *(UINTN
*)(TssBase
+ TSS_X64_IST1_OFFSET
) = (mSmmStackArrayBase
+ EFI_PAGE_SIZE
+ Index
* (mSmmStackSize
+ mSmmShadowStackSize
));
100 *GdtStepSize
= GdtTableStepSize
;
105 Get Protected mode code segment from current GDT table.
107 @return Protected mode code segment value.
114 IA32_DESCRIPTOR GdtrDesc
;
115 IA32_SEGMENT_DESCRIPTOR
*GdtEntry
;
119 AsmReadGdtr (&GdtrDesc
);
120 GdtEntryCount
= (GdtrDesc
.Limit
+ 1) / sizeof (IA32_SEGMENT_DESCRIPTOR
);
121 GdtEntry
= (IA32_SEGMENT_DESCRIPTOR
*) GdtrDesc
.Base
;
122 for (Index
= 0; Index
< GdtEntryCount
; Index
++) {
123 if (GdtEntry
->Bits
.L
== 0) {
124 if (GdtEntry
->Bits
.Type
> 8 && GdtEntry
->Bits
.DB
== 1) {
130 ASSERT (Index
!= GdtEntryCount
);
135 Transfer AP to safe hlt-loop after it finished restore CPU features on S3 patch.
137 @param[in] ApHltLoopCode The address of the safe hlt-loop function.
138 @param[in] TopOfStack A pointer to the new stack to use for the ApHltLoopCode.
139 @param[in] NumberToFinishAddress Address of Semaphore of APs finish count.
143 TransferApToSafeState (
144 IN UINTN ApHltLoopCode
,
146 IN UINTN NumberToFinishAddress
150 GetProtectedModeCS (),
151 (UINT32
)ApHltLoopCode
,
152 (UINT32
)NumberToFinishAddress
,
157 // It should never reach here
163 Initialize the shadow stack related data structure.
165 @param CpuIndex The index of CPU.
166 @param ShadowStack The bottom of the shadow stack for this CPU.
174 UINTN SmmShadowStackSize
;
175 UINT64
*InterruptSspTable
;
178 if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask
) != 0) && mCetSupported
) {
179 SmmShadowStackSize
= EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize
)));
181 // Add 1 page as known good shadow stack
183 SmmShadowStackSize
+= EFI_PAGES_TO_SIZE (1);
185 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
187 // Add one guard page between Known Good Shadow Stack and SMM Shadow Stack.
189 SmmShadowStackSize
+= EFI_PAGES_TO_SIZE (1);
191 mCetPl0Ssp
= (UINT32
)((UINTN
)ShadowStack
+ SmmShadowStackSize
- sizeof(UINT64
));
192 PatchInstructionX86 (mPatchCetPl0Ssp
, mCetPl0Ssp
, 4);
193 DEBUG ((DEBUG_INFO
, "mCetPl0Ssp - 0x%x\n", mCetPl0Ssp
));
194 DEBUG ((DEBUG_INFO
, "ShadowStack - 0x%x\n", ShadowStack
));
195 DEBUG ((DEBUG_INFO
, " SmmShadowStackSize - 0x%x\n", SmmShadowStackSize
));
197 if (mSmmInterruptSspTables
== 0) {
198 mSmmInterruptSspTables
= (UINTN
)AllocateZeroPool(sizeof(UINT64
) * 8 * gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
);
199 ASSERT (mSmmInterruptSspTables
!= 0);
200 DEBUG ((DEBUG_INFO
, "mSmmInterruptSspTables - 0x%x\n", mSmmInterruptSspTables
));
204 // The highest address on the stack (0xFE0) is a save-previous-ssp token pointing to a location that is 40 bytes away - 0xFB8.
205 // The supervisor shadow stack token is just above it at address 0xFD8. This is where the interrupt SSP table points.
206 // So when an interrupt of exception occurs, we can use SAVESSP/RESTORESSP/CLEARSSBUSY for the supervisor shadow stack,
207 // due to the reason the RETF in SMM exception handler cannot clear the BUSY flag with same CPL.
208 // (only IRET or RETF with different CPL can clear BUSY flag)
209 // Please refer to UefiCpuPkg/Library/CpuExceptionHandlerLib/X64 for the full stack frame at runtime.
210 // According to SDM (ver. 075 June 2021), shadow stack should be 32 bytes aligned.
212 InterruptSsp
= (UINT32
)(((UINTN
)ShadowStack
+ EFI_PAGES_TO_SIZE(1) - (sizeof(UINT64
) * 4)) & ~0x1f);
213 *(UINT64
*)(UINTN
)InterruptSsp
= (InterruptSsp
- sizeof(UINT64
) * 4) | 0x2;
214 mCetInterruptSsp
= InterruptSsp
- sizeof(UINT64
);
216 mCetInterruptSspTable
= (UINT32
)(UINTN
)(mSmmInterruptSspTables
+ sizeof(UINT64
) * 8 * CpuIndex
);
217 InterruptSspTable
= (UINT64
*)(UINTN
)mCetInterruptSspTable
;
218 InterruptSspTable
[1] = mCetInterruptSsp
;
219 PatchInstructionX86 (mPatchCetInterruptSsp
, mCetInterruptSsp
, 4);
220 PatchInstructionX86 (mPatchCetInterruptSspTable
, mCetInterruptSspTable
, 4);
221 DEBUG ((DEBUG_INFO
, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp
));
222 DEBUG ((DEBUG_INFO
, "mCetInterruptSspTable - 0x%x\n", mCetInterruptSspTable
));