]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/PlatformPei/AmdSev.c
OvmfPkg/PlatformPei: register GHCB gpa for the SEV-SNP guest
[mirror_edk2.git] / OvmfPkg / PlatformPei / AmdSev.c
CommitLineData
13b5d743
BS
1/**@file\r
2 Initialize Secure Encrypted Virtualization (SEV) support\r
3\r
45388d04 4 Copyright (c) 2017 - 2020, Advanced Micro Devices. All rights reserved.<BR>\r
13b5d743 5\r
b26f0cf9 6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
13b5d743
BS
7\r
8**/\r
9//\r
10// The package level header files this module uses\r
11//\r
300aae11 12#include <IndustryStandard/Q35MchIch9.h>\r
449a6e49 13#include <Library/BaseMemoryLib.h>\r
13b5d743 14#include <Library/DebugLib.h>\r
86defc2c 15#include <Library/HobLib.h>\r
6d576e7a 16#include <Library/MemEncryptSevLib.h>\r
449a6e49 17#include <Library/MemoryAllocationLib.h>\r
13b5d743 18#include <Library/PcdLib.h>\r
6d576e7a 19#include <PiPei.h>\r
449a6e49 20#include <Register/Amd/Msr.h>\r
300aae11 21#include <Register/Intel/SmramSaveStateMap.h>\r
a19b6489 22#include <Library/VmgExitLib.h>\r
13b5d743 23\r
c0d221a3
LE
24#include "Platform.h"\r
25\r
a19b6489
BS
26/**\r
27 Handle an SEV-SNP/GHCB protocol check failure.\r
28\r
29 Notify the hypervisor using the VMGEXIT instruction that the SEV-SNP guest\r
30 wishes to be terminated.\r
31\r
32 @param[in] ReasonCode Reason code to provide to the hypervisor for the\r
33 termination request.\r
34\r
35**/\r
36STATIC\r
37VOID\r
38SevEsProtocolFailure (\r
39 IN UINT8 ReasonCode\r
40 )\r
41{\r
42 MSR_SEV_ES_GHCB_REGISTER Msr;\r
43\r
44 //\r
45 // Use the GHCB MSR Protocol to request termination by the hypervisor\r
46 //\r
47 Msr.GhcbPhysicalAddress = 0;\r
48 Msr.GhcbTerminate.Function = GHCB_INFO_TERMINATE_REQUEST;\r
49 Msr.GhcbTerminate.ReasonCodeSet = GHCB_TERMINATE_GHCB;\r
50 Msr.GhcbTerminate.ReasonCode = ReasonCode;\r
51 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);\r
52\r
53 AsmVmgExit ();\r
54\r
55 ASSERT (FALSE);\r
56 CpuDeadLoop ();\r
57}\r
58\r
59/**\r
60\r
61 This function can be used to register the GHCB GPA.\r
62\r
63 @param[in] Address The physical address to be registered.\r
64\r
65**/\r
66STATIC\r
67VOID\r
68GhcbRegister (\r
69 IN EFI_PHYSICAL_ADDRESS Address\r
70 )\r
71{\r
72 MSR_SEV_ES_GHCB_REGISTER Msr;\r
73 MSR_SEV_ES_GHCB_REGISTER CurrentMsr;\r
74\r
75 //\r
76 // Save the current MSR Value\r
77 //\r
78 CurrentMsr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
79\r
80 //\r
81 // Use the GHCB MSR Protocol to request to register the GPA.\r
82 //\r
83 Msr.GhcbPhysicalAddress = Address & ~EFI_PAGE_MASK;\r
84 Msr.GhcbGpaRegister.Function = GHCB_INFO_GHCB_GPA_REGISTER_REQUEST;\r
85 AsmWriteMsr64 (MSR_SEV_ES_GHCB, Msr.GhcbPhysicalAddress);\r
86\r
87 AsmVmgExit ();\r
88\r
89 Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB);\r
90\r
91 //\r
92 // If hypervisor responded with a different GPA than requested then fail.\r
93 //\r
94 if ((Msr.GhcbGpaRegister.Function != GHCB_INFO_GHCB_GPA_REGISTER_RESPONSE) ||\r
95 ((Msr.GhcbPhysicalAddress & ~EFI_PAGE_MASK) != Address))\r
96 {\r
97 SevEsProtocolFailure (GHCB_TERMINATE_GHCB_GENERAL);\r
98 }\r
99\r
100 //\r
101 // Restore the MSR\r
102 //\r
103 AsmWriteMsr64 (MSR_SEV_ES_GHCB, CurrentMsr.GhcbPhysicalAddress);\r
104}\r
105\r
cf845a74
TL
106/**\r
107\r
108 Initialize SEV-ES support if running as an SEV-ES guest.\r
109\r
110 **/\r
111STATIC\r
112VOID\r
113AmdSevEsInitialize (\r
114 VOID\r
115 )\r
116{\r
5667dc43
TL
117 UINT8 *GhcbBase;\r
118 PHYSICAL_ADDRESS GhcbBasePa;\r
119 UINTN GhcbPageCount;\r
120 UINT8 *GhcbBackupBase;\r
121 UINT8 *GhcbBackupPages;\r
122 UINTN GhcbBackupPageCount;\r
123 SEV_ES_PER_CPU_DATA *SevEsData;\r
124 UINTN PageCount;\r
125 RETURN_STATUS PcdStatus, DecryptStatus;\r
126 IA32_DESCRIPTOR Gdtr;\r
127 VOID *Gdt;\r
cf845a74
TL
128\r
129 if (!MemEncryptSevEsIsEnabled ()) {\r
130 return;\r
131 }\r
132\r
133 PcdStatus = PcdSetBoolS (PcdSevEsIsEnabled, TRUE);\r
134 ASSERT_RETURN_ERROR (PcdStatus);\r
449a6e49
TL
135\r
136 //\r
137 // Allocate GHCB and per-CPU variable pages.\r
3b49d0a5
TL
138 // Since the pages must survive across the UEFI to OS transition\r
139 // make them reserved.\r
449a6e49
TL
140 //\r
141 GhcbPageCount = mMaxCpuCount * 2;\r
ac0a286f 142 GhcbBase = AllocateReservedPages (GhcbPageCount);\r
449a6e49
TL
143 ASSERT (GhcbBase != NULL);\r
144\r
ac0a286f 145 GhcbBasePa = (PHYSICAL_ADDRESS)(UINTN)GhcbBase;\r
449a6e49
TL
146\r
147 //\r
148 // Each vCPU gets two consecutive pages, the first is the GHCB and the\r
149 // second is the per-CPU variable page. Loop through the allocation and\r
150 // only clear the encryption mask for the GHCB pages.\r
151 //\r
152 for (PageCount = 0; PageCount < GhcbPageCount; PageCount += 2) {\r
153 DecryptStatus = MemEncryptSevClearPageEncMask (\r
ac0a286f
MK
154 0,\r
155 GhcbBasePa + EFI_PAGES_TO_SIZE (PageCount),\r
156 1\r
157 );\r
449a6e49
TL
158 ASSERT_RETURN_ERROR (DecryptStatus);\r
159 }\r
160\r
161 ZeroMem (GhcbBase, EFI_PAGES_TO_SIZE (GhcbPageCount));\r
162\r
163 PcdStatus = PcdSet64S (PcdGhcbBase, GhcbBasePa);\r
164 ASSERT_RETURN_ERROR (PcdStatus);\r
165 PcdStatus = PcdSet64S (PcdGhcbSize, EFI_PAGES_TO_SIZE (GhcbPageCount));\r
166 ASSERT_RETURN_ERROR (PcdStatus);\r
167\r
ac0a286f
MK
168 DEBUG ((\r
169 DEBUG_INFO,\r
449a6e49 170 "SEV-ES is enabled, %lu GHCB pages allocated starting at 0x%p\n",\r
ac0a286f
MK
171 (UINT64)GhcbPageCount,\r
172 GhcbBase\r
173 ));\r
449a6e49 174\r
5667dc43
TL
175 //\r
176 // Allocate #VC recursion backup pages. The number of backup pages needed is\r
177 // one less than the maximum VC count.\r
178 //\r
179 GhcbBackupPageCount = mMaxCpuCount * (VMGEXIT_MAXIMUM_VC_COUNT - 1);\r
ac0a286f 180 GhcbBackupBase = AllocatePages (GhcbBackupPageCount);\r
5667dc43
TL
181 ASSERT (GhcbBackupBase != NULL);\r
182\r
183 GhcbBackupPages = GhcbBackupBase;\r
184 for (PageCount = 1; PageCount < GhcbPageCount; PageCount += 2) {\r
185 SevEsData =\r
186 (SEV_ES_PER_CPU_DATA *)(GhcbBase + EFI_PAGES_TO_SIZE (PageCount));\r
187 SevEsData->GhcbBackupPages = GhcbBackupPages;\r
188\r
189 GhcbBackupPages += EFI_PAGE_SIZE * (VMGEXIT_MAXIMUM_VC_COUNT - 1);\r
190 }\r
191\r
ac0a286f
MK
192 DEBUG ((\r
193 DEBUG_INFO,\r
5667dc43 194 "SEV-ES is enabled, %lu GHCB backup pages allocated starting at 0x%p\n",\r
ac0a286f
MK
195 (UINT64)GhcbBackupPageCount,\r
196 GhcbBackupBase\r
197 ));\r
5667dc43 198\r
a19b6489
BS
199 //\r
200 // SEV-SNP guest requires that GHCB GPA must be registered before using it.\r
201 //\r
202 if (MemEncryptSevSnpIsEnabled ()) {\r
203 GhcbRegister (GhcbBasePa);\r
204 }\r
205\r
449a6e49 206 AsmWriteMsr64 (MSR_SEV_ES_GHCB, GhcbBasePa);\r
13ed9e5f
TL
207\r
208 //\r
209 // The SEV support will clear the C-bit from non-RAM areas. The early GDT\r
210 // lives in a non-RAM area, so when an exception occurs (like a #VC) the GDT\r
211 // will be read as un-encrypted even though it was created before the C-bit\r
212 // was cleared (encrypted). This will result in a failure to be able to\r
213 // handle the exception.\r
214 //\r
215 AsmReadGdtr (&Gdtr);\r
216\r
ac0a286f 217 Gdt = AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)Gdtr.Limit + 1));\r
13ed9e5f
TL
218 ASSERT (Gdt != NULL);\r
219\r
ac0a286f
MK
220 CopyMem (Gdt, (VOID *)Gdtr.Base, Gdtr.Limit + 1);\r
221 Gdtr.Base = (UINTN)Gdt;\r
13ed9e5f 222 AsmWriteGdtr (&Gdtr);\r
cf845a74
TL
223}\r
224\r
13b5d743
BS
225/**\r
226\r
227 Function checks if SEV support is available, if present then it sets\r
228 the dynamic PcdPteMemoryEncryptionAddressOrMask with memory encryption mask.\r
229\r
230 **/\r
231VOID\r
13b5d743
BS
232AmdSevInitialize (\r
233 VOID\r
234 )\r
235{\r
ac0a286f
MK
236 UINT64 EncryptionMask;\r
237 RETURN_STATUS PcdStatus;\r
13b5d743
BS
238\r
239 //\r
240 // Check if SEV is enabled\r
241 //\r
242 if (!MemEncryptSevIsEnabled ()) {\r
243 return;\r
244 }\r
245\r
13b5d743
BS
246 //\r
247 // Set Memory Encryption Mask PCD\r
248 //\r
45388d04 249 EncryptionMask = MemEncryptSevGetEncryptionMask ();\r
ac0a286f 250 PcdStatus = PcdSet64S (PcdPteMemoryEncryptionAddressOrMask, EncryptionMask);\r
13b5d743
BS
251 ASSERT_RETURN_ERROR (PcdStatus);\r
252\r
253 DEBUG ((DEBUG_INFO, "SEV is enabled (mask 0x%lx)\n", EncryptionMask));\r
6041ac65
BS
254\r
255 //\r
256 // Set Pcd to Deny the execution of option ROM when security\r
257 // violation.\r
258 //\r
259 PcdStatus = PcdSet32S (PcdOptionRomImageVerificationPolicy, 0x4);\r
260 ASSERT_RETURN_ERROR (PcdStatus);\r
86defc2c
LE
261\r
262 //\r
263 // When SMM is required, cover the pages containing the initial SMRAM Save\r
264 // State Map with a memory allocation HOB:\r
265 //\r
266 // There's going to be a time interval between our decrypting those pages for\r
267 // SMBASE relocation and re-encrypting the same pages after SMBASE\r
268 // relocation. We shall ensure that the DXE phase stay away from those pages\r
269 // until after re-encryption, in order to prevent an information leak to the\r
270 // hypervisor.\r
271 //\r
272 if (FeaturePcdGet (PcdSmmSmramRequire) && (mBootMode != BOOT_ON_S3_RESUME)) {\r
ac0a286f
MK
273 RETURN_STATUS LocateMapStatus;\r
274 UINTN MapPagesBase;\r
275 UINTN MapPagesCount;\r
86defc2c
LE
276\r
277 LocateMapStatus = MemEncryptSevLocateInitialSmramSaveStateMapPages (\r
278 &MapPagesBase,\r
279 &MapPagesCount\r
280 );\r
281 ASSERT_RETURN_ERROR (LocateMapStatus);\r
282\r
300aae11
LE
283 if (mQ35SmramAtDefaultSmbase) {\r
284 //\r
285 // The initial SMRAM Save State Map has been covered as part of a larger\r
286 // reserved memory allocation in InitializeRamRegions().\r
287 //\r
288 ASSERT (SMM_DEFAULT_SMBASE <= MapPagesBase);\r
289 ASSERT (\r
290 (MapPagesBase + EFI_PAGES_TO_SIZE (MapPagesCount) <=\r
291 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE)\r
292 );\r
293 } else {\r
294 BuildMemoryAllocationHob (\r
295 MapPagesBase, // BaseAddress\r
296 EFI_PAGES_TO_SIZE (MapPagesCount), // Length\r
297 EfiBootServicesData // MemoryType\r
298 );\r
299 }\r
86defc2c 300 }\r
cf845a74
TL
301\r
302 //\r
303 // Check and perform SEV-ES initialization if required.\r
304 //\r
305 AmdSevEsInitialize ();\r
13b5d743 306}\r