]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Library/MpInitLib/AmdSev.c
UefiCpuPkg/MpInitLib: move SEV specific routines in AmdSev.c
[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
11#include <Library/VmgExitLib.h>\r
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
113 if (CpuMpData->WakeupBufferHigh != 0) {\r
114 APResetFn = (AP_RESET *)(CpuMpData->WakeupBufferHigh + CpuMpData->AddressMap.SwitchToRealNoNxOffset);\r
115 } else {\r
116 APResetFn = (AP_RESET *)(CpuMpData->MpCpuExchangeInfo->BufferStart + CpuMpData->AddressMap.SwitchToRealOffset);\r
117 }\r
118\r
119 BufferStart = CpuMpData->MpCpuExchangeInfo->BufferStart;\r
120 StackStart = CpuMpData->SevEsAPResetStackStart -\r
121 (AP_RESET_STACK_SIZE * ProcessorNumber);\r
122\r
123 //\r
124 // This call never returns.\r
125 //\r
126 APResetFn (BufferStart, Code16, Code32, StackStart);\r
127}\r
128\r
129/**\r
130 Allocate the SEV-ES AP jump table buffer.\r
131\r
132 @param[in, out] CpuMpData The pointer to CPU MP Data structure.\r
133**/\r
134VOID\r
135AllocateSevEsAPMemory (\r
136 IN OUT CPU_MP_DATA *CpuMpData\r
137 )\r
138{\r
139 if (CpuMpData->SevEsAPBuffer == (UINTN)-1) {\r
140 CpuMpData->SevEsAPBuffer =\r
141 CpuMpData->SevEsIsEnabled ? GetSevEsAPMemory () : 0;\r
142 }\r
143}\r
144\r
145/**\r
146 Program the SEV-ES AP jump table buffer.\r
147\r
148 @param[in] SipiVector The SIPI vector used for the AP Reset\r
149**/\r
150VOID\r
151SetSevEsJumpTable (\r
152 IN UINTN SipiVector\r
153 )\r
154{\r
155 SEV_ES_AP_JMP_FAR *JmpFar;\r
156 UINT32 Offset, InsnByte;\r
157 UINT8 LoNib, HiNib;\r
158\r
159 JmpFar = (SEV_ES_AP_JMP_FAR *)(UINTN)FixedPcdGet32 (PcdSevEsWorkAreaBase);\r
160 ASSERT (JmpFar != NULL);\r
161\r
162 //\r
163 // Obtain the address of the Segment/Rip location in the workarea.\r
164 // This will be set to a value derived from the SIPI vector and will\r
165 // be the memory address used for the far jump below.\r
166 //\r
167 Offset = FixedPcdGet32 (PcdSevEsWorkAreaBase);\r
168 Offset += sizeof (JmpFar->InsnBuffer);\r
169 LoNib = (UINT8)Offset;\r
170 HiNib = (UINT8)(Offset >> 8);\r
171\r
172 //\r
173 // Program the workarea (which is the initial AP boot address) with\r
174 // far jump to the SIPI vector (where XX and YY represent the\r
175 // address of where the SIPI vector is stored.\r
176 //\r
177 // JMP FAR [CS:XXYY] => 2E FF 2E YY XX\r
178 //\r
179 InsnByte = 0;\r
180 JmpFar->InsnBuffer[InsnByte++] = 0x2E; // CS override prefix\r
181 JmpFar->InsnBuffer[InsnByte++] = 0xFF; // JMP (FAR)\r
182 JmpFar->InsnBuffer[InsnByte++] = 0x2E; // ModRM (JMP memory location)\r
183 JmpFar->InsnBuffer[InsnByte++] = LoNib; // YY offset ...\r
184 JmpFar->InsnBuffer[InsnByte++] = HiNib; // XX offset ...\r
185\r
186 //\r
187 // Program the Segment/Rip based on the SIPI vector (always at least\r
188 // 16-byte aligned, so Rip is set to 0).\r
189 //\r
190 JmpFar->Rip = 0;\r
191 JmpFar->Segment = (UINT16)(SipiVector >> 4);\r
192}\r
193\r
194/**\r
195 The function puts the AP in halt loop.\r
196\r
197 @param[in] CpuMpData The pointer to CPU MP Data structure.\r
198**/\r
199VOID\r
200SevEsPlaceApHlt (\r
201 CPU_MP_DATA *CpuMpData\r
202 )\r
203{\r
204 MSR_SEV_ES_GHCB_REGISTER Msr;\r
205 GHCB *Ghcb;\r
206 UINT64 Status;\r
207 BOOLEAN DoDecrement;\r
208 BOOLEAN InterruptState;\r
209\r
210 DoDecrement = (BOOLEAN)(CpuMpData->InitFlag == ApInitConfig);\r
211\r
212 while (TRUE) {\r
213 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
214 Ghcb = Msr.Ghcb;\r
215\r
216 VmgInit (Ghcb, &InterruptState);\r
217\r
218 if (DoDecrement) {\r
219 DoDecrement = FALSE;\r
220\r
221 //\r
222 // Perform the delayed decrement just before issuing the first\r
223 // VMGEXIT with AP_RESET_HOLD.\r
224 //\r
225 InterlockedDecrement ((UINT32 *)&CpuMpData->MpCpuExchangeInfo->NumApsExecuting);\r
226 }\r
227\r
228 Status = VmgExit (Ghcb, SVM_EXIT_AP_RESET_HOLD, 0, 0);\r
229 if ((Status == 0) && (Ghcb->SaveArea.SwExitInfo2 != 0)) {\r
230 VmgDone (Ghcb, InterruptState);\r
231 break;\r
232 }\r
233\r
234 VmgDone (Ghcb, InterruptState);\r
235 }\r
236\r
237 //\r
238 // Awakened in a new phase? Use the new CpuMpData\r
239 //\r
240 if (CpuMpData->NewCpuMpData != NULL) {\r
241 CpuMpData = CpuMpData->NewCpuMpData;\r
242 }\r
243\r
244 MpInitLibSevEsAPReset (Ghcb, CpuMpData);\r
245}\r