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