1 /** @file
2   SMM CPU misc functions for Ia32 arch specific.
3 
4 Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "PiSmmCpuDxeSmm.h"
10 
11 extern UINT64 gTaskGateDescriptor;
12 
13 EFI_PHYSICAL_ADDRESS                mGdtBuffer;
14 UINTN                               mGdtBufferSize;
15 
16 extern BOOLEAN mCetSupported;
17 extern UINTN mSmmShadowStackSize;
18 
19 X86_ASSEMBLY_PATCH_LABEL mPatchCetPl0Ssp;
20 X86_ASSEMBLY_PATCH_LABEL mPatchCetInterruptSsp;
21 UINT32 mCetPl0Ssp;
22 UINT32 mCetInterruptSsp;
23 
24 /**
25   Initialize IDT for SMM Stack Guard.
26 
27 **/
28 VOID
29 EFIAPI
InitializeIDTSmmStackGuard(VOID)30 InitializeIDTSmmStackGuard (
31   VOID
32   )
33 {
34   IA32_IDT_GATE_DESCRIPTOR  *IdtGate;
35 
36   //
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.
40   //
41   IdtGate = (IA32_IDT_GATE_DESCRIPTOR *)gcSmiIdtr.Base;
42   IdtGate += EXCEPT_IA32_PAGE_FAULT;
43   IdtGate->Uint64 = gTaskGateDescriptor;
44 }
45 
46 /**
47   Initialize Gdt for all processors.
48 
49   @param[in]   Cr3          CR3 value.
50   @param[out]  GdtStepSize  The step size for GDT table.
51 
52   @return GdtBase for processor 0.
53           GdtBase for processor X is: GdtBase + (GdtStepSize * X)
54 **/
55 VOID *
InitGdt(IN UINTN Cr3,OUT UINTN * GdtStepSize)56 InitGdt (
57   IN  UINTN  Cr3,
58   OUT UINTN  *GdtStepSize
59   )
60 {
61   UINTN                     Index;
62   IA32_SEGMENT_DESCRIPTOR   *GdtDescriptor;
63   UINTN                     TssBase;
64   UINTN                     GdtTssTableSize;
65   UINT8                     *GdtTssTables;
66   UINTN                     GdtTableStepSize;
67   UINTN                     InterruptShadowStack;
68 
69   if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
70     //
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
73     // on each SMI entry.
74     //
75 
76     //
77     // Enlarge GDT to contain 2 TSS descriptors
78     //
79     gcSmiGdtr.Limit += (UINT16)(2 * sizeof (IA32_SEGMENT_DESCRIPTOR));
80 
81     GdtTssTableSize = (gcSmiGdtr.Limit + 1 + TSS_SIZE + EXCEPTION_TSS_SIZE + 7) & ~7; // 8 bytes aligned
82     mGdtBufferSize = GdtTssTableSize * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;
83     //
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.
87     //
88     GdtTssTables = (UINT8*)AllocatePages (EFI_SIZE_TO_PAGES (mGdtBufferSize));
89     ASSERT (GdtTssTables != NULL);
90     mGdtBuffer = (UINTN)GdtTssTables;
91     GdtTableStepSize = GdtTssTableSize;
92 
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);
95       //
96       // Fixup TSS descriptors
97       //
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);
103 
104       TssBase += TSS_SIZE;
105       GdtDescriptor++;
106       GdtDescriptor->Bits.BaseLow = (UINT16)TssBase;
107       GdtDescriptor->Bits.BaseMid = (UINT8)(TssBase >> 16);
108       GdtDescriptor->Bits.BaseHigh = (UINT8)(TssBase >> 24);
109       //
110       // Fixup TSS segments
111       //
112       // ESP as known good stack
113       //
114       *(UINTN *)(TssBase + TSS_IA32_ESP_OFFSET) =  mSmmStackArrayBase + EFI_PAGE_SIZE + Index * mSmmStackSize;
115       *(UINT32 *)(TssBase + TSS_IA32_CR3_OFFSET) = Cr3;
116 
117       //
118       // Setup ShadowStack for stack switch
119       //
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;
123       }
124     }
125   } else {
126     //
127     // Just use original table, AllocatePage and copy them here to make sure GDTs are covered in page memory.
128     //
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;
135 
136     for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) {
137       CopyMem (GdtTssTables + GdtTableStepSize * Index, (VOID*)(UINTN)gcSmiGdtr.Base, gcSmiGdtr.Limit + 1);
138     }
139   }
140 
141   *GdtStepSize = GdtTableStepSize;
142   return GdtTssTables;
143 }
144 
145 /**
146   Transfer AP to safe hlt-loop after it finished restore CPU features on S3 patch.
147 
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.
151 
152 **/
153 VOID
TransferApToSafeState(IN UINTN ApHltLoopCode,IN UINTN TopOfStack,IN UINTN NumberToFinishAddress)154 TransferApToSafeState (
155   IN UINTN  ApHltLoopCode,
156   IN UINTN  TopOfStack,
157   IN UINTN  NumberToFinishAddress
158   )
159 {
160   SwitchStack (
161     (SWITCH_STACK_ENTRY_POINT)ApHltLoopCode,
162     (VOID *)NumberToFinishAddress,
163     NULL,
164     (VOID *)TopOfStack
165     );
166   //
167   // It should never reach here
168   //
169   ASSERT (FALSE);
170 }
171 
172 /**
173   Initialize the shadow stack related data structure.
174 
175   @param CpuIndex     The index of CPU.
176   @param ShadowStack  The bottom of the shadow stack for this CPU.
177 **/
178 VOID
InitShadowStack(IN UINTN CpuIndex,IN VOID * ShadowStack)179 InitShadowStack (
180   IN UINTN  CpuIndex,
181   IN VOID   *ShadowStack
182   )
183 {
184   UINTN       SmmShadowStackSize;
185 
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);
190     }
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));
196 
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));
201     }
202   }
203 }
204 
205