2 SMM CPU misc functions for Ia32 arch specific.
4 Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include "PiSmmCpuDxeSmm.h"
17 extern UINT64 gTaskGateDescriptor
;
19 EFI_PHYSICAL_ADDRESS mGdtBuffer
;
22 extern BOOLEAN mCetSupported
;
23 extern UINTN mSmmShadowStackSize
;
25 X86_ASSEMBLY_PATCH_LABEL mPatchCetPl0Ssp
;
26 X86_ASSEMBLY_PATCH_LABEL mPatchCetInterruptSsp
;
28 UINT32 mCetInterruptSsp
;
31 Initialize IDT for SMM Stack Guard.
36 InitializeIDTSmmStackGuard (
40 IA32_IDT_GATE_DESCRIPTOR
*IdtGate
;
43 // If SMM Stack Guard feature is enabled, the Page Fault Exception entry in IDT
44 // is a Task Gate Descriptor so that when a Page Fault Exception occurs,
45 // the processors can use a known good stack in case stack is ran out.
47 IdtGate
= (IA32_IDT_GATE_DESCRIPTOR
*)gcSmiIdtr
.Base
;
48 IdtGate
+= EXCEPT_IA32_PAGE_FAULT
;
49 IdtGate
->Uint64
= gTaskGateDescriptor
;
53 Initialize Gdt for all processors.
55 @param[in] Cr3 CR3 value.
56 @param[out] GdtStepSize The step size for GDT table.
58 @return GdtBase for processor 0.
59 GdtBase for processor X is: GdtBase + (GdtStepSize * X)
64 OUT UINTN
*GdtStepSize
68 IA32_SEGMENT_DESCRIPTOR
*GdtDescriptor
;
70 UINTN GdtTssTableSize
;
72 UINTN GdtTableStepSize
;
73 UINTN InterruptShadowStack
;
75 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
77 // For IA32 SMM, if SMM Stack Guard feature is enabled, we use 2 TSS.
78 // in this case, we allocate separate GDT/TSS for each CPUs to avoid TSS load contention
83 // Enlarge GDT to contain 2 TSS descriptors
85 gcSmiGdtr
.Limit
+= (UINT16
)(2 * sizeof (IA32_SEGMENT_DESCRIPTOR
));
87 GdtTssTableSize
= (gcSmiGdtr
.Limit
+ 1 + TSS_SIZE
+ EXCEPTION_TSS_SIZE
+ 7) & ~7; // 8 bytes aligned
88 mGdtBufferSize
= GdtTssTableSize
* gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
;
90 // IA32 Stack Guard need use task switch to switch stack that need
91 // write GDT and TSS, so AllocateCodePages() could not be used here
92 // as code pages will be set to RO.
94 GdtTssTables
= (UINT8
*)AllocatePages (EFI_SIZE_TO_PAGES (mGdtBufferSize
));
95 ASSERT (GdtTssTables
!= NULL
);
96 mGdtBuffer
= (UINTN
)GdtTssTables
;
97 GdtTableStepSize
= GdtTssTableSize
;
99 for (Index
= 0; Index
< gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
; Index
++) {
100 CopyMem (GdtTssTables
+ GdtTableStepSize
* Index
, (VOID
*)(UINTN
)gcSmiGdtr
.Base
, gcSmiGdtr
.Limit
+ 1 + TSS_SIZE
+ EXCEPTION_TSS_SIZE
);
102 // Fixup TSS descriptors
104 TssBase
= (UINTN
)(GdtTssTables
+ GdtTableStepSize
* Index
+ gcSmiGdtr
.Limit
+ 1);
105 GdtDescriptor
= (IA32_SEGMENT_DESCRIPTOR
*)(TssBase
) - 2;
106 GdtDescriptor
->Bits
.BaseLow
= (UINT16
)TssBase
;
107 GdtDescriptor
->Bits
.BaseMid
= (UINT8
)(TssBase
>> 16);
108 GdtDescriptor
->Bits
.BaseHigh
= (UINT8
)(TssBase
>> 24);
112 GdtDescriptor
->Bits
.BaseLow
= (UINT16
)TssBase
;
113 GdtDescriptor
->Bits
.BaseMid
= (UINT8
)(TssBase
>> 16);
114 GdtDescriptor
->Bits
.BaseHigh
= (UINT8
)(TssBase
>> 24);
116 // Fixup TSS segments
118 // ESP as known good stack
120 *(UINTN
*)(TssBase
+ TSS_IA32_ESP_OFFSET
) = mSmmStackArrayBase
+ EFI_PAGE_SIZE
+ Index
* mSmmStackSize
;
121 *(UINT32
*)(TssBase
+ TSS_IA32_CR3_OFFSET
) = Cr3
;
124 // Setup ShadowStack for stack switch
126 if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask
) != 0) && mCetSupported
) {
127 InterruptShadowStack
= (UINTN
)(mSmmStackArrayBase
+ mSmmStackSize
+ EFI_PAGES_TO_SIZE (1) - sizeof(UINT64
) + (mSmmStackSize
+ mSmmShadowStackSize
) * Index
);
128 *(UINT32
*)(TssBase
+ TSS_IA32_SSP_OFFSET
) = (UINT32
)InterruptShadowStack
;
133 // Just use original table, AllocatePage and copy them here to make sure GDTs are covered in page memory.
135 GdtTssTableSize
= gcSmiGdtr
.Limit
+ 1;
136 mGdtBufferSize
= GdtTssTableSize
* gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
;
137 GdtTssTables
= (UINT8
*)AllocateCodePages (EFI_SIZE_TO_PAGES (mGdtBufferSize
));
138 ASSERT (GdtTssTables
!= NULL
);
139 mGdtBuffer
= (UINTN
)GdtTssTables
;
140 GdtTableStepSize
= GdtTssTableSize
;
142 for (Index
= 0; Index
< gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
; Index
++) {
143 CopyMem (GdtTssTables
+ GdtTableStepSize
* Index
, (VOID
*)(UINTN
)gcSmiGdtr
.Base
, gcSmiGdtr
.Limit
+ 1);
147 *GdtStepSize
= GdtTableStepSize
;
152 Transfer AP to safe hlt-loop after it finished restore CPU features on S3 patch.
154 @param[in] ApHltLoopCode The address of the safe hlt-loop function.
155 @param[in] TopOfStack A pointer to the new stack to use for the ApHltLoopCode.
156 @param[in] NumberToFinishAddress Address of Semaphore of APs finish count.
160 TransferApToSafeState (
161 IN UINTN ApHltLoopCode
,
163 IN UINTN NumberToFinishAddress
167 (SWITCH_STACK_ENTRY_POINT
)ApHltLoopCode
,
168 (VOID
*)NumberToFinishAddress
,
173 // It should never reach here
179 Initialize the shadow stack related data structure.
181 @param CpuIndex The index of CPU.
182 @param ShadowStack The bottom of the shadow stack for this CPU.
190 UINTN SmmShadowStackSize
;
192 if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask
) != 0) && mCetSupported
) {
193 SmmShadowStackSize
= EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize
)));
194 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
195 SmmShadowStackSize
+= EFI_PAGES_TO_SIZE (2);
197 mCetPl0Ssp
= (UINT32
)((UINTN
)ShadowStack
+ SmmShadowStackSize
- sizeof(UINT64
));
198 PatchInstructionX86 (mPatchCetPl0Ssp
, mCetPl0Ssp
, 4);
199 DEBUG ((DEBUG_INFO
, "mCetPl0Ssp - 0x%x\n", mCetPl0Ssp
));
200 DEBUG ((DEBUG_INFO
, "ShadowStack - 0x%x\n", ShadowStack
));
201 DEBUG ((DEBUG_INFO
, " SmmShadowStackSize - 0x%x\n", SmmShadowStackSize
));
203 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
204 mCetInterruptSsp
= (UINT32
)((UINTN
)ShadowStack
+ EFI_PAGES_TO_SIZE(1) - sizeof(UINT64
));
205 PatchInstructionX86 (mPatchCetInterruptSsp
, mCetInterruptSsp
, 4);
206 DEBUG ((DEBUG_INFO
, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp
));