2 SMM CPU misc functions for Ia32 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 extern UINT64 gTaskGateDescriptor
;
13 EFI_PHYSICAL_ADDRESS mGdtBuffer
;
16 extern BOOLEAN mCetSupported
;
17 extern UINTN mSmmShadowStackSize
;
19 X86_ASSEMBLY_PATCH_LABEL mPatchCetPl0Ssp
;
20 X86_ASSEMBLY_PATCH_LABEL mPatchCetInterruptSsp
;
22 UINT32 mCetInterruptSsp
;
25 Initialize IDT for SMM Stack Guard.
30 InitializeIDTSmmStackGuard (
34 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
37 // If SMM Stack Guard feature is enabled, the Page Fault Exception entry in IDT
38 // is a Task Gate Descriptor so that when a Page Fault Exception occurs,
39 // the processors can use a known good stack in case stack is ran out.
41 IdtGate
= (IA32_IDT_GATE_DESCRIPTOR
*)gcSmiIdtr
.Base
;
42 IdtGate
+= EXCEPT_IA32_PAGE_FAULT
;
43 IdtGate
->Uint64
= gTaskGateDescriptor
;
47 Initialize Gdt for all processors.
49 @param[in] Cr3 CR3 value.
50 @param[out] GdtStepSize The step size for GDT table.
52 @return GdtBase for processor 0.
53 GdtBase for processor X is: GdtBase + (GdtStepSize * X)
58 OUT UINTN
*GdtStepSize
62 IA32_SEGMENT_DESCRIPTOR
*GdtDescriptor
;
64 UINTN GdtTssTableSize
;
66 UINTN GdtTableStepSize
;
67 UINTN InterruptShadowStack
;
69 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
71 // For IA32 SMM, if SMM Stack Guard feature is enabled, we use 2 TSS.
72 // in this case, we allocate separate GDT/TSS for each CPUs to avoid TSS load contention
77 // Enlarge GDT to contain 2 TSS descriptors
79 gcSmiGdtr
.Limit
+= (UINT16
)(2 * sizeof (IA32_SEGMENT_DESCRIPTOR
));
81 GdtTssTableSize
= (gcSmiGdtr
.Limit
+ 1 + TSS_SIZE
+ EXCEPTION_TSS_SIZE
+ 7) & ~7; // 8 bytes aligned
82 mGdtBufferSize
= GdtTssTableSize
* gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
;
84 // IA32 Stack Guard need use task switch to switch stack that need
85 // write GDT and TSS, so AllocateCodePages() could not be used here
86 // as code pages will be set to RO.
88 GdtTssTables
= (UINT8
*)AllocatePages (EFI_SIZE_TO_PAGES (mGdtBufferSize
));
89 ASSERT (GdtTssTables
!= NULL
);
90 mGdtBuffer
= (UINTN
)GdtTssTables
;
91 GdtTableStepSize
= GdtTssTableSize
;
93 for (Index
= 0; Index
< gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
; Index
++) {
94 CopyMem (GdtTssTables
+ GdtTableStepSize
* Index
, (VOID
*)(UINTN
)gcSmiGdtr
.Base
, gcSmiGdtr
.Limit
+ 1 + TSS_SIZE
+ EXCEPTION_TSS_SIZE
);
96 // Fixup TSS descriptors
98 TssBase
= (UINTN
)(GdtTssTables
+ GdtTableStepSize
* Index
+ gcSmiGdtr
.Limit
+ 1);
99 GdtDescriptor
= (IA32_SEGMENT_DESCRIPTOR
*)(TssBase
) - 2;
100 GdtDescriptor
->Bits
.BaseLow
= (UINT16
)TssBase
;
101 GdtDescriptor
->Bits
.BaseMid
= (UINT8
)(TssBase
>> 16);
102 GdtDescriptor
->Bits
.BaseHigh
= (UINT8
)(TssBase
>> 24);
106 GdtDescriptor
->Bits
.BaseLow
= (UINT16
)TssBase
;
107 GdtDescriptor
->Bits
.BaseMid
= (UINT8
)(TssBase
>> 16);
108 GdtDescriptor
->Bits
.BaseHigh
= (UINT8
)(TssBase
>> 24);
110 // Fixup TSS segments
112 // ESP as known good stack
114 *(UINTN
*)(TssBase
+ TSS_IA32_ESP_OFFSET
) = mSmmStackArrayBase
+ EFI_PAGE_SIZE
+ Index
* mSmmStackSize
;
115 *(UINT32
*)(TssBase
+ TSS_IA32_CR3_OFFSET
) = Cr3
;
118 // Setup ShadowStack for stack switch
120 if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask
) != 0) && mCetSupported
) {
121 InterruptShadowStack
= (UINTN
)(mSmmStackArrayBase
+ mSmmStackSize
+ EFI_PAGES_TO_SIZE (1) - sizeof(UINT64
) + (mSmmStackSize
+ mSmmShadowStackSize
) * Index
);
122 *(UINT32
*)(TssBase
+ TSS_IA32_SSP_OFFSET
) = (UINT32
)InterruptShadowStack
;
127 // Just use original table, AllocatePage and copy them here to make sure GDTs are covered in page memory.
129 GdtTssTableSize
= gcSmiGdtr
.Limit
+ 1;
130 mGdtBufferSize
= GdtTssTableSize
* gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
;
131 GdtTssTables
= (UINT8
*)AllocateCodePages (EFI_SIZE_TO_PAGES (mGdtBufferSize
));
132 ASSERT (GdtTssTables
!= NULL
);
133 mGdtBuffer
= (UINTN
)GdtTssTables
;
134 GdtTableStepSize
= GdtTssTableSize
;
136 for (Index
= 0; Index
< gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
; Index
++) {
137 CopyMem (GdtTssTables
+ GdtTableStepSize
* Index
, (VOID
*)(UINTN
)gcSmiGdtr
.Base
, gcSmiGdtr
.Limit
+ 1);
141 *GdtStepSize
= GdtTableStepSize
;
146 Transfer AP to safe hlt-loop after it finished restore CPU features on S3 patch.
148 @param[in] ApHltLoopCode The address of the safe hlt-loop function.
149 @param[in] TopOfStack A pointer to the new stack to use for the ApHltLoopCode.
150 @param[in] NumberToFinishAddress Address of Semaphore of APs finish count.
154 TransferApToSafeState (
155 IN UINTN ApHltLoopCode
,
157 IN UINTN NumberToFinishAddress
161 (SWITCH_STACK_ENTRY_POINT
)ApHltLoopCode
,
162 (VOID
*)NumberToFinishAddress
,
167 // It should never reach here
173 Initialize the shadow stack related data structure.
175 @param CpuIndex The index of CPU.
176 @param ShadowStack The bottom of the shadow stack for this CPU.
184 UINTN SmmShadowStackSize
;
186 if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask
) != 0) && mCetSupported
) {
187 SmmShadowStackSize
= EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize
)));
188 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
189 SmmShadowStackSize
+= EFI_PAGES_TO_SIZE (2);
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 (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
198 mCetInterruptSsp
= (UINT32
)((UINTN
)ShadowStack
+ EFI_PAGES_TO_SIZE(1) - sizeof(UINT64
));
199 PatchInstructionX86 (mPatchCetInterruptSsp
, mCetInterruptSsp
, 4);
200 DEBUG ((DEBUG_INFO
, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp
));