1 | /** @file
|
---|
2 | CPU MP Initialize helper function for AMD SEV.
|
---|
3 |
|
---|
4 | Copyright (c) 2021, AMD Inc. All rights reserved.<BR>
|
---|
5 |
|
---|
6 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
7 |
|
---|
8 | **/
|
---|
9 |
|
---|
10 | #include "MpLib.h"
|
---|
11 | #include <Library/CcExitLib.h>
|
---|
12 |
|
---|
13 | /**
|
---|
14 | Get Protected mode code segment with 16-bit default addressing
|
---|
15 | from current GDT table.
|
---|
16 |
|
---|
17 | @return Protected mode 16-bit code segment value.
|
---|
18 | **/
|
---|
19 | STATIC
|
---|
20 | UINT16
|
---|
21 | GetProtectedMode16CS (
|
---|
22 | VOID
|
---|
23 | )
|
---|
24 | {
|
---|
25 | IA32_DESCRIPTOR GdtrDesc;
|
---|
26 | IA32_SEGMENT_DESCRIPTOR *GdtEntry;
|
---|
27 | UINTN GdtEntryCount;
|
---|
28 | UINT16 Index;
|
---|
29 |
|
---|
30 | Index = (UINT16)-1;
|
---|
31 | AsmReadGdtr (&GdtrDesc);
|
---|
32 | GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
|
---|
33 | GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;
|
---|
34 | for (Index = 0; Index < GdtEntryCount; Index++) {
|
---|
35 | if ((GdtEntry->Bits.L == 0) &&
|
---|
36 | (GdtEntry->Bits.DB == 0) &&
|
---|
37 | (GdtEntry->Bits.Type > 8))
|
---|
38 | {
|
---|
39 | break;
|
---|
40 | }
|
---|
41 |
|
---|
42 | GdtEntry++;
|
---|
43 | }
|
---|
44 |
|
---|
45 | ASSERT (Index != GdtEntryCount);
|
---|
46 | return Index * 8;
|
---|
47 | }
|
---|
48 |
|
---|
49 | /**
|
---|
50 | Get Protected mode code segment with 32-bit default addressing
|
---|
51 | from current GDT table.
|
---|
52 |
|
---|
53 | @return Protected mode 32-bit code segment value.
|
---|
54 | **/
|
---|
55 | STATIC
|
---|
56 | UINT16
|
---|
57 | GetProtectedMode32CS (
|
---|
58 | VOID
|
---|
59 | )
|
---|
60 | {
|
---|
61 | IA32_DESCRIPTOR GdtrDesc;
|
---|
62 | IA32_SEGMENT_DESCRIPTOR *GdtEntry;
|
---|
63 | UINTN GdtEntryCount;
|
---|
64 | UINT16 Index;
|
---|
65 |
|
---|
66 | Index = (UINT16)-1;
|
---|
67 | AsmReadGdtr (&GdtrDesc);
|
---|
68 | GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
|
---|
69 | GdtEntry = (IA32_SEGMENT_DESCRIPTOR *)GdtrDesc.Base;
|
---|
70 | for (Index = 0; Index < GdtEntryCount; Index++) {
|
---|
71 | if ((GdtEntry->Bits.L == 0) &&
|
---|
72 | (GdtEntry->Bits.DB == 1) &&
|
---|
73 | (GdtEntry->Bits.Type > 8))
|
---|
74 | {
|
---|
75 | break;
|
---|
76 | }
|
---|
77 |
|
---|
78 | GdtEntry++;
|
---|
79 | }
|
---|
80 |
|
---|
81 | ASSERT (Index != GdtEntryCount);
|
---|
82 | return Index * 8;
|
---|
83 | }
|
---|
84 |
|
---|
85 | /**
|
---|
86 | Reset an AP when in SEV-ES mode.
|
---|
87 |
|
---|
88 | If successful, this function never returns.
|
---|
89 |
|
---|
90 | @param[in] Ghcb Pointer to the GHCB
|
---|
91 | @param[in] CpuMpData Pointer to CPU MP Data
|
---|
92 |
|
---|
93 | **/
|
---|
94 | VOID
|
---|
95 | MpInitLibSevEsAPReset (
|
---|
96 | IN GHCB *Ghcb,
|
---|
97 | IN CPU_MP_DATA *CpuMpData
|
---|
98 | )
|
---|
99 | {
|
---|
100 | EFI_STATUS Status;
|
---|
101 | UINTN ProcessorNumber;
|
---|
102 | UINT16 Code16, Code32;
|
---|
103 | AP_RESET *APResetFn;
|
---|
104 | UINTN BufferStart;
|
---|
105 | UINTN StackStart;
|
---|
106 |
|
---|
107 | Status = GetProcessorNumber (CpuMpData, &ProcessorNumber);
|
---|
108 | ASSERT_EFI_ERROR (Status);
|
---|
109 |
|
---|
110 | Code16 = GetProtectedMode16CS ();
|
---|
111 | Code32 = GetProtectedMode32CS ();
|
---|
112 |
|
---|
113 | APResetFn = (AP_RESET *)(CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);
|
---|
114 |
|
---|
115 | BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;
|
---|
116 | StackStart = CpuMpData->SevEsAPResetStackStart -
|
---|
117 | (AP_RESET_STACK_SIZE * ProcessorNumber);
|
---|
118 |
|
---|
119 | //
|
---|
120 | // This call never returns.
|
---|
121 | //
|
---|
122 | APResetFn (BufferStart, Code16, Code32, StackStart);
|
---|
123 | }
|
---|
124 |
|
---|
125 | /**
|
---|
126 | Allocate the SEV-ES AP jump table buffer.
|
---|
127 |
|
---|
128 | @param[in, out] CpuMpData The pointer to CPU MP Data structure.
|
---|
129 | **/
|
---|
130 | VOID
|
---|
131 | AllocateSevEsAPMemory (
|
---|
132 | IN OUT CPU_MP_DATA *CpuMpData
|
---|
133 | )
|
---|
134 | {
|
---|
135 | if (CpuMpData->SevEsAPBuffer == (UINTN)-1) {
|
---|
136 | CpuMpData->SevEsAPBuffer =
|
---|
137 | CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;
|
---|
138 | }
|
---|
139 | }
|
---|
140 |
|
---|
141 | /**
|
---|
142 | Program the SEV-ES AP jump table buffer.
|
---|
143 |
|
---|
144 | @param[in] SipiVector The SIPI vector used for the AP Reset
|
---|
145 | **/
|
---|
146 | VOID
|
---|
147 | SetSevEsJumpTable (
|
---|
148 | IN UINTN SipiVector
|
---|
149 | )
|
---|
150 | {
|
---|
151 | SEV_ES_AP_JMP_FAR *JmpFar;
|
---|
152 | UINT32 Offset, InsnByte;
|
---|
153 | UINT8 LoNib, HiNib;
|
---|
154 |
|
---|
155 | JmpFar = (SEV_ES_AP_JMP_FAR *)(UINTN)FixedPcdGet32 (PcdSevEsWorkAreaBase);
|
---|
156 | ASSERT (JmpFar != NULL);
|
---|
157 |
|
---|
158 | //
|
---|
159 | // Obtain the address of the Segment/Rip location in the workarea.
|
---|
160 | // This will be set to a value derived from the SIPI vector and will
|
---|
161 | // be the memory address used for the far jump below.
|
---|
162 | //
|
---|
163 | Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);
|
---|
164 | Offset += sizeof (JmpFar->InsnBuffer);
|
---|
165 | LoNib = (UINT8)Offset;
|
---|
166 | HiNib = (UINT8)(Offset >> 8);
|
---|
167 |
|
---|
168 | //
|
---|
169 | // Program the workarea (which is the initial AP boot address) with
|
---|
170 | // far jump to the SIPI vector (where XX and YY represent the
|
---|
171 | // address of where the SIPI vector is stored.
|
---|
172 | //
|
---|
173 | // JMP FAR [CS:XXYY] => 2E FF 2E YY XX
|
---|
174 | //
|
---|
175 | InsnByte = 0;
|
---|
176 | JmpFar->InsnBuffer[InsnByte++] = 0x2E; // CS override prefix
|
---|
177 | JmpFar->InsnBuffer[InsnByte++] = 0xFF; // JMP (FAR)
|
---|
178 | JmpFar->InsnBuffer[InsnByte++] = 0x2E; // ModRM (JMP memory location)
|
---|
179 | JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...
|
---|
180 | JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...
|
---|
181 |
|
---|
182 | //
|
---|
183 | // Program the Segment/Rip based on the SIPI vector (always at least
|
---|
184 | // 16-byte aligned, so Rip is set to 0).
|
---|
185 | //
|
---|
186 | JmpFar->Rip = 0;
|
---|
187 | JmpFar->Segment = (UINT16)(SipiVector >> 4);
|
---|
188 | }
|
---|
189 |
|
---|
190 | /**
|
---|
191 | The function puts the AP in halt loop.
|
---|
192 |
|
---|
193 | @param[in] CpuMpData The pointer to CPU MP Data structure.
|
---|
194 | **/
|
---|
195 | VOID
|
---|
196 | SevEsPlaceApHlt (
|
---|
197 | CPU_MP_DATA *CpuMpData
|
---|
198 | )
|
---|
199 | {
|
---|
200 | MSR_SEV_ES_GHCB_REGISTER Msr;
|
---|
201 | GHCB *Ghcb;
|
---|
202 | UINT64 Status;
|
---|
203 | BOOLEAN DoDecrement;
|
---|
204 | BOOLEAN InterruptState;
|
---|
205 |
|
---|
206 | DoDecrement = (BOOLEAN)(CpuMpData->InitFlag == ApInitConfig);
|
---|
207 |
|
---|
208 | while (TRUE) {
|
---|
209 | Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);
|
---|
210 | Ghcb = Msr.Ghcb;
|
---|
211 |
|
---|
212 | CcExitVmgInit (Ghcb, &InterruptState);
|
---|
213 |
|
---|
214 | if (DoDecrement) {
|
---|
215 | DoDecrement = FALSE;
|
---|
216 |
|
---|
217 | //
|
---|
218 | // Perform the delayed decrement just before issuing the first
|
---|
219 | // VMGEXIT with AP_RESET_HOLD.
|
---|
220 | //
|
---|
221 | InterlockedDecrement ((UINT32 *)&CpuMpData->MpCpuExchangeInfo->NumApsExecuting);
|
---|
222 | }
|
---|
223 |
|
---|
224 | Status = CcExitVmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);
|
---|
225 | if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {
|
---|
226 | CcExitVmgDone (Ghcb, InterruptState);
|
---|
227 | break;
|
---|
228 | }
|
---|
229 |
|
---|
230 | CcExitVmgDone (Ghcb, InterruptState);
|
---|
231 | }
|
---|
232 |
|
---|
233 | //
|
---|
234 | // Awakened in a new phase? Use the new CpuMpData
|
---|
235 | //
|
---|
236 | if (CpuMpData->NewCpuMpData != NULL) {
|
---|
237 | CpuMpData = CpuMpData->NewCpuMpData;
|
---|
238 | }
|
---|
239 |
|
---|
240 | MpInitLibSevEsAPReset (Ghcb, CpuMpData);
|
---|
241 | }
|
---|
242 |
|
---|
243 | /**
|
---|
244 | The function fills the exchange data for the AP.
|
---|
245 |
|
---|
246 | @param[in] ExchangeInfo The pointer to CPU Exchange Data structure
|
---|
247 | **/
|
---|
248 | VOID
|
---|
249 | FillExchangeInfoDataSevEs (
|
---|
250 | IN volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo
|
---|
251 | )
|
---|
252 | {
|
---|
253 | UINT32 StdRangeMax;
|
---|
254 |
|
---|
255 | AsmCpuid (CPUID_SIGNATURE, &StdRangeMax, NULL, NULL, NULL);
|
---|
256 | if (StdRangeMax >= CPUID_EXTENDED_TOPOLOGY) {
|
---|
257 | CPUID_EXTENDED_TOPOLOGY_EBX ExtTopoEbx;
|
---|
258 |
|
---|
259 | AsmCpuid (CPUID_EXTENDED_TOPOLOGY, NULL, &ExtTopoEbx.Uint32, NULL, NULL);
|
---|
260 | ExchangeInfo->ExtTopoAvail = !!ExtTopoEbx.Bits.LogicalProcessors;
|
---|
261 | }
|
---|
262 | }
|
---|