]> git.proxmox.com Git - mirror_edk2.git/blame - UefiCpuPkg/Library/MpInitLib/X64/AmdSev.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / Library / MpInitLib / X64 / AmdSev.c
CommitLineData
06544455
TL
1/** @file\r
2\r
3 AMD SEV helper function.\r
4\r
5 Copyright (c) 2021, AMD Incorporated. All rights reserved.<BR>\r
6\r
7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
8\r
9**/\r
10\r
11#include "MpLib.h"\r
a89f558d 12#include <Library/CcExitLib.h>\r
06544455
TL
13#include <Register/Amd/Fam17Msr.h>\r
14#include <Register/Amd/Ghcb.h>\r
15\r
16/**\r
17 Create an SEV-SNP AP save area (VMSA) for use in running the vCPU.\r
18\r
19 @param[in] CpuMpData Pointer to CPU MP Data\r
20 @param[in] CpuData Pointer to CPU AP Data\r
21 @param[in] ApicId APIC ID of the vCPU\r
22**/\r
23VOID\r
24SevSnpCreateSaveArea (\r
25 IN CPU_MP_DATA *CpuMpData,\r
26 IN CPU_AP_DATA *CpuData,\r
27 UINT32 ApicId\r
28 )\r
29{\r
30 SEV_ES_SAVE_AREA *SaveArea;\r
31 IA32_CR0 ApCr0;\r
32 IA32_CR0 ResetCr0;\r
33 IA32_CR4 ApCr4;\r
34 IA32_CR4 ResetCr4;\r
35 UINTN StartIp;\r
36 UINT8 SipiVector;\r
37 UINT32 RmpAdjustStatus;\r
38 UINT64 VmgExitStatus;\r
39 MSR_SEV_ES_GHCB_REGISTER Msr;\r
40 GHCB *Ghcb;\r
41 BOOLEAN InterruptState;\r
42 UINT64 ExitInfo1;\r
43 UINT64 ExitInfo2;\r
44\r
45 //\r
46 // Allocate a single page for the SEV-ES Save Area and initialize it.\r
47 //\r
48 SaveArea = AllocateReservedPages (1);\r
49 if (!SaveArea) {\r
50 return;\r
51 }\r
52\r
53 ZeroMem (SaveArea, EFI_PAGE_SIZE);\r
54\r
55 //\r
56 // Propogate the CR0.NW and CR0.CD setting to the AP\r
57 //\r
58 ResetCr0.UintN = 0x00000010;\r
59 ApCr0.UintN = CpuData->VolatileRegisters.Cr0;\r
60 if (ApCr0.Bits.NW) {\r
61 ResetCr0.Bits.NW = 1;\r
62 }\r
63\r
64 if (ApCr0.Bits.CD) {\r
65 ResetCr0.Bits.CD = 1;\r
66 }\r
67\r
68 //\r
69 // Propagate the CR4.MCE setting to the AP\r
70 //\r
71 ResetCr4.UintN = 0;\r
72 ApCr4.UintN = CpuData->VolatileRegisters.Cr4;\r
73 if (ApCr4.Bits.MCE) {\r
74 ResetCr4.Bits.MCE = 1;\r
75 }\r
76\r
77 //\r
78 // Convert the start IP into a SIPI Vector\r
79 //\r
80 StartIp = CpuMpData->MpCpuExchangeInfo->BufferStart;\r
81 SipiVector = (UINT8)(StartIp >> 12);\r
82\r
83 //\r
84 // Set the CS:RIP value based on the start IP\r
85 //\r
86 SaveArea->Cs.Base = SipiVector << 12;\r
87 SaveArea->Cs.Selector = SipiVector << 8;\r
88 SaveArea->Cs.Limit = 0xFFFF;\r
89 SaveArea->Cs.Attributes.Bits.Present = 1;\r
90 SaveArea->Cs.Attributes.Bits.Sbit = 1;\r
91 SaveArea->Cs.Attributes.Bits.Type = SEV_ES_RESET_CODE_SEGMENT_TYPE;\r
92 SaveArea->Rip = StartIp & 0xFFF;\r
93\r
94 //\r
95 // Set the remaining values as defined in APM for INIT\r
96 //\r
97 SaveArea->Ds.Limit = 0xFFFF;\r
98 SaveArea->Ds.Attributes.Bits.Present = 1;\r
99 SaveArea->Ds.Attributes.Bits.Sbit = 1;\r
100 SaveArea->Ds.Attributes.Bits.Type = SEV_ES_RESET_DATA_SEGMENT_TYPE;\r
101 SaveArea->Es = SaveArea->Ds;\r
102 SaveArea->Fs = SaveArea->Ds;\r
103 SaveArea->Gs = SaveArea->Ds;\r
104 SaveArea->Ss = SaveArea->Ds;\r
105\r
106 SaveArea->Gdtr.Limit = 0xFFFF;\r
107 SaveArea->Ldtr.Limit = 0xFFFF;\r
108 SaveArea->Ldtr.Attributes.Bits.Present = 1;\r
109 SaveArea->Ldtr.Attributes.Bits.Type = SEV_ES_RESET_LDT_TYPE;\r
110 SaveArea->Idtr.Limit = 0xFFFF;\r
111 SaveArea->Tr.Limit = 0xFFFF;\r
112 SaveArea->Ldtr.Attributes.Bits.Present = 1;\r
113 SaveArea->Ldtr.Attributes.Bits.Type = SEV_ES_RESET_TSS_TYPE;\r
114\r
115 SaveArea->Efer = 0x1000;\r
116 SaveArea->Cr4 = ResetCr4.UintN;\r
117 SaveArea->Cr0 = ResetCr0.UintN;\r
118 SaveArea->Dr7 = 0x0400;\r
119 SaveArea->Dr6 = 0xFFFF0FF0;\r
120 SaveArea->Rflags = 0x0002;\r
121 SaveArea->GPat = 0x0007040600070406ULL;\r
122 SaveArea->XCr0 = 0x0001;\r
123 SaveArea->Mxcsr = 0x1F80;\r
124 SaveArea->X87Ftw = 0x5555;\r
125 SaveArea->X87Fcw = 0x0040;\r
126\r
127 //\r
128 // Set the SEV-SNP specific fields for the save area:\r
129 // VMPL - always VMPL0\r
130 // SEV_FEATURES - equivalent to the SEV_STATUS MSR right shifted 2 bits\r
131 //\r
132 SaveArea->Vmpl = 0;\r
133 SaveArea->SevFeatures = AsmReadMsr64 (MSR_SEV_STATUS) >> 2;\r
134\r
135 //\r
136 // To turn the page into a recognized VMSA page, issue RMPADJUST:\r
137 // Target VMPL but numerically higher than current VMPL\r
138 // Target PermissionMask is not used\r
139 //\r
140 RmpAdjustStatus = SevSnpRmpAdjust (\r
141 (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,\r
142 TRUE\r
143 );\r
144 ASSERT (RmpAdjustStatus == 0);\r
145\r
146 ExitInfo1 = (UINT64)ApicId << 32;\r
147 ExitInfo1 |= SVM_VMGEXIT_SNP_AP_CREATE;\r
148 ExitInfo2 = (UINT64)(UINTN)SaveArea;\r
149\r
150 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
151 Ghcb = Msr.Ghcb;\r
152\r
765ba5bf 153 CcExitVmgInit (Ghcb, &InterruptState);\r
06544455 154 Ghcb->SaveArea.Rax = SaveArea->SevFeatures;\r
765ba5bf
MX
155 CcExitVmgSetOffsetValid (Ghcb, GhcbRax);\r
156 VmgExitStatus = CcExitVmgExit (\r
06544455
TL
157 Ghcb,\r
158 SVM_EXIT_SNP_AP_CREATION,\r
159 ExitInfo1,\r
160 ExitInfo2\r
161 );\r
765ba5bf 162 CcExitVmgDone (Ghcb, InterruptState);\r
06544455
TL
163\r
164 ASSERT (VmgExitStatus == 0);\r
165 if (VmgExitStatus != 0) {\r
166 RmpAdjustStatus = SevSnpRmpAdjust (\r
167 (EFI_PHYSICAL_ADDRESS)(UINTN)SaveArea,\r
168 FALSE\r
169 );\r
170 if (RmpAdjustStatus == 0) {\r
171 FreePages (SaveArea, 1);\r
172 } else {\r
173 DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));\r
174 }\r
175\r
176 SaveArea = NULL;\r
177 }\r
178\r
179 if (CpuData->SevEsSaveArea) {\r
180 RmpAdjustStatus = SevSnpRmpAdjust (\r
181 (EFI_PHYSICAL_ADDRESS)(UINTN)CpuData->SevEsSaveArea,\r
182 FALSE\r
183 );\r
184 if (RmpAdjustStatus == 0) {\r
185 FreePages (CpuData->SevEsSaveArea, 1);\r
186 } else {\r
187 DEBUG ((DEBUG_INFO, "SEV-SNP: RMPADJUST failed, leaking VMSA page\n"));\r
188 }\r
189 }\r
190\r
191 CpuData->SevEsSaveArea = SaveArea;\r
192}\r
193\r
194/**\r
195 Create SEV-SNP APs.\r
196\r
197 @param[in] CpuMpData Pointer to CPU MP Data\r
198 @param[in] ProcessorNumber The handle number of specified processor\r
199 (-1 for all APs)\r
200**/\r
201VOID\r
202SevSnpCreateAP (\r
203 IN CPU_MP_DATA *CpuMpData,\r
204 IN INTN ProcessorNumber\r
205 )\r
206{\r
207 CPU_INFO_IN_HOB *CpuInfoInHob;\r
208 CPU_AP_DATA *CpuData;\r
209 UINTN Index;\r
210 UINT32 ApicId;\r
211\r
212 ASSERT (CpuMpData->MpCpuExchangeInfo->BufferStart < 0x100000);\r
213\r
214 CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob;\r
215\r
216 if (ProcessorNumber < 0) {\r
217 for (Index = 0; Index < CpuMpData->CpuCount; Index++) {\r
218 if (Index != CpuMpData->BspNumber) {\r
219 CpuData = &CpuMpData->CpuData[Index];\r
220 ApicId = CpuInfoInHob[Index].ApicId,\r
221 SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);\r
222 }\r
223 }\r
224 } else {\r
225 Index = (UINTN)ProcessorNumber;\r
226 CpuData = &CpuMpData->CpuData[Index];\r
227 ApicId = CpuInfoInHob[ProcessorNumber].ApicId,\r
228 SevSnpCreateSaveArea (CpuMpData, CpuData, ApicId);\r
229 }\r
230}\r
231\r
232/**\r
233 Issue RMPADJUST to adjust the VMSA attribute of an SEV-SNP page.\r
234\r
235 @param[in] PageAddress\r
236 @param[in] VmsaPage\r
237\r
238 @return RMPADJUST return value\r
239**/\r
240UINT32\r
241SevSnpRmpAdjust (\r
242 IN EFI_PHYSICAL_ADDRESS PageAddress,\r
243 IN BOOLEAN VmsaPage\r
244 )\r
245{\r
246 UINT64 Rdx;\r
247\r
248 //\r
249 // The RMPADJUST instruction is used to set or clear the VMSA bit for a\r
250 // page. The VMSA change is only made when running at VMPL0 and is ignored\r
251 // otherwise. If too low a target VMPL is specified, the instruction can\r
252 // succeed without changing the VMSA bit when not running at VMPL0. Using a\r
253 // target VMPL level of 1, RMPADJUST will return a FAIL_PERMISSION error if\r
254 // not running at VMPL0, thus ensuring that the VMSA bit is set appropriately\r
255 // when no error is returned.\r
256 //\r
257 Rdx = 1;\r
258 if (VmsaPage) {\r
259 Rdx |= RMPADJUST_VMSA_PAGE_BIT;\r
260 }\r
261\r
262 return AsmRmpAdjust ((UINT64)PageAddress, 0, Rdx);\r
263}\r