]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/PlatformInitLib/IntelTdx.c
OvmfPkg: Update PlatformInitLib to process Tdx hoblist
[mirror_edk2.git] / OvmfPkg / Library / PlatformInitLib / IntelTdx.c
CommitLineData
b22ac35b
MX
1/** @file\r
2 Initialize Intel TDX support.\r
3\r
4 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>\r
5\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10#include <PiPei.h>\r
11#include <Library/BaseLib.h>\r
12#include <Library/DebugLib.h>\r
13#include <Library/HobLib.h>\r
14#include <Library/BaseMemoryLib.h>\r
15#include <Library/MemoryAllocationLib.h>\r
16#include <IndustryStandard/Tdx.h>\r
17#include <IndustryStandard/IntelTdx.h>\r
18#include <IndustryStandard/QemuFwCfg.h>\r
19#include <Library/QemuFwCfgLib.h>\r
20#include <Library/PeiServicesLib.h>\r
21#include <Library/TdxLib.h>\r
22#include <Library/SynchronizationLib.h>\r
23#include <WorkArea.h>\r
24#include <ConfidentialComputingGuestAttr.h>\r
25\r
26#define ALIGNED_2MB_MASK 0x1fffff\r
27#define EFI_RESOURCE_MEMORY_UNACCEPTED 7\r
28\r
29/**\r
30 This function will be called to accept pages. Only BSP accepts pages.\r
31\r
32 TDCALL(ACCEPT_PAGE) supports the accept page size of 4k and 2M. To\r
33 simplify the implementation, the Memory to be accpeted is splitted\r
34 into 3 parts:\r
35 ----------------- <-- StartAddress1 (not 2M aligned)\r
36 | part 1 | Length1 < 2M\r
37 |---------------| <-- StartAddress2 (2M aligned)\r
38 | | Length2 = Integer multiples of 2M\r
39 | part 2 |\r
40 | |\r
41 |---------------| <-- StartAddress3\r
42 | part 3 | Length3 < 2M\r
43 |---------------|\r
44\r
45 @param[in] PhysicalAddress Start physical adress\r
46 @param[in] PhysicalEnd End physical address\r
47\r
48 @retval EFI_SUCCESS Accept memory successfully\r
49 @retval Others Other errors as indicated\r
50**/\r
51EFI_STATUS\r
52EFIAPI\r
53BspAcceptMemoryResourceRange (\r
54 IN EFI_PHYSICAL_ADDRESS PhysicalAddress,\r
55 IN EFI_PHYSICAL_ADDRESS PhysicalEnd\r
56 )\r
57{\r
58 EFI_STATUS Status;\r
59 UINT32 AcceptPageSize;\r
60 UINT64 StartAddress1;\r
61 UINT64 StartAddress2;\r
62 UINT64 StartAddress3;\r
63 UINT64 TotalLength;\r
64 UINT64 Length1;\r
65 UINT64 Length2;\r
66 UINT64 Length3;\r
67 UINT64 Pages;\r
68\r
69 AcceptPageSize = FixedPcdGet32 (PcdTdxAcceptPageSize);\r
70 TotalLength = PhysicalEnd - PhysicalAddress;\r
71 StartAddress1 = 0;\r
72 StartAddress2 = 0;\r
73 StartAddress3 = 0;\r
74 Length1 = 0;\r
75 Length2 = 0;\r
76 Length3 = 0;\r
77\r
78 if (TotalLength == 0) {\r
79 return EFI_SUCCESS;\r
80 }\r
81\r
82 DEBUG ((DEBUG_INFO, "TdAccept: 0x%llx - 0x%llx\n", PhysicalAddress, TotalLength));\r
83\r
84 if (ALIGN_VALUE (PhysicalAddress, SIZE_2MB) != PhysicalAddress) {\r
85 StartAddress1 = PhysicalAddress;\r
86 Length1 = ALIGN_VALUE (PhysicalAddress, SIZE_2MB) - PhysicalAddress;\r
87 if (Length1 >= TotalLength) {\r
88 Length1 = TotalLength;\r
89 }\r
90\r
91 PhysicalAddress += Length1;\r
92 TotalLength -= Length1;\r
93 }\r
94\r
95 if (TotalLength > SIZE_2MB) {\r
96 StartAddress2 = PhysicalAddress;\r
97 Length2 = TotalLength & ~(UINT64)ALIGNED_2MB_MASK;\r
98 PhysicalAddress += Length2;\r
99 TotalLength -= Length2;\r
100 }\r
101\r
102 if (TotalLength) {\r
103 StartAddress3 = PhysicalAddress;\r
104 Length3 = TotalLength;\r
105 }\r
106\r
107 DEBUG ((DEBUG_INFO, " Part1: 0x%llx - 0x%llx\n", StartAddress1, Length1));\r
108 DEBUG ((DEBUG_INFO, " Part2: 0x%llx - 0x%llx\n", StartAddress2, Length2));\r
109 DEBUG ((DEBUG_INFO, " Part3: 0x%llx - 0x%llx\n", StartAddress3, Length3));\r
110 DEBUG ((DEBUG_INFO, " Page : 0x%x\n", AcceptPageSize));\r
111\r
112 Status = EFI_SUCCESS;\r
113 if (Length1 > 0) {\r
114 Pages = Length1 / SIZE_4KB;\r
115 Status = TdAcceptPages (StartAddress1, Pages, SIZE_4KB);\r
116 if (EFI_ERROR (Status)) {\r
117 return Status;\r
118 }\r
119 }\r
120\r
121 if (Length2 > 0) {\r
122 Pages = Length2 / AcceptPageSize;\r
123 Status = TdAcceptPages (StartAddress2, Pages, AcceptPageSize);\r
124 if (EFI_ERROR (Status)) {\r
125 return Status;\r
126 }\r
127 }\r
128\r
129 if (Length3 > 0) {\r
130 Pages = Length3 / SIZE_4KB;\r
131 Status = TdAcceptPages (StartAddress3, Pages, SIZE_4KB);\r
132 ASSERT (!EFI_ERROR (Status));\r
133 if (EFI_ERROR (Status)) {\r
134 return Status;\r
135 }\r
136 }\r
137\r
138 return Status;\r
139}\r
140\r
141/**\r
142 Check the value whether in the valid list.\r
143\r
144 @param[in] Value A value\r
145 @param[in] ValidList A pointer to valid list\r
146 @param[in] ValidListLength Length of valid list\r
147\r
148 @retval TRUE The value is in valid list.\r
149 @retval FALSE The value is not in valid list.\r
150\r
151**/\r
152BOOLEAN\r
153EFIAPI\r
154IsInValidList (\r
155 IN UINT32 Value,\r
156 IN UINT32 *ValidList,\r
157 IN UINT32 ValidListLength\r
158 )\r
159{\r
160 UINT32 index;\r
161\r
162 if (ValidList == NULL) {\r
163 return FALSE;\r
164 }\r
165\r
166 for (index = 0; index < ValidListLength; index++) {\r
167 if (ValidList[index] == Value) {\r
168 return TRUE;\r
169 }\r
170 }\r
171\r
172 return FALSE;\r
173}\r
174\r
175/**\r
176 Check the integrity of VMM Hob List.\r
177\r
178 @param[in] VmmHobList A pointer to Hob List\r
179\r
180 @retval TRUE The Hob List is valid.\r
181 @retval FALSE The Hob List is invalid.\r
182\r
183**/\r
184BOOLEAN\r
185EFIAPI\r
186ValidateHobList (\r
187 IN CONST VOID *VmmHobList\r
188 )\r
189{\r
190 EFI_PEI_HOB_POINTERS Hob;\r
191 UINT32 EFI_BOOT_MODE_LIST[] = {\r
192 BOOT_WITH_FULL_CONFIGURATION,\r
193 BOOT_WITH_MINIMAL_CONFIGURATION,\r
194 BOOT_ASSUMING_NO_CONFIGURATION_CHANGES,\r
195 BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS,\r
196 BOOT_WITH_DEFAULT_SETTINGS,\r
197 BOOT_ON_S4_RESUME,\r
198 BOOT_ON_S5_RESUME,\r
199 BOOT_WITH_MFG_MODE_SETTINGS,\r
200 BOOT_ON_S2_RESUME,\r
201 BOOT_ON_S3_RESUME,\r
202 BOOT_ON_FLASH_UPDATE,\r
203 BOOT_IN_RECOVERY_MODE\r
204 };\r
205\r
206 UINT32 EFI_RESOURCE_TYPE_LIST[] = {\r
207 EFI_RESOURCE_SYSTEM_MEMORY,\r
208 EFI_RESOURCE_MEMORY_MAPPED_IO,\r
209 EFI_RESOURCE_IO,\r
210 EFI_RESOURCE_FIRMWARE_DEVICE,\r
211 EFI_RESOURCE_MEMORY_MAPPED_IO_PORT,\r
212 EFI_RESOURCE_MEMORY_RESERVED,\r
213 EFI_RESOURCE_IO_RESERVED,\r
214 EFI_RESOURCE_MEMORY_UNACCEPTED\r
215 };\r
216\r
217 if (VmmHobList == NULL) {\r
218 DEBUG ((DEBUG_ERROR, "HOB: HOB data pointer is NULL\n"));\r
219 return FALSE;\r
220 }\r
221\r
222 Hob.Raw = (UINT8 *)VmmHobList;\r
223\r
224 //\r
225 // Parse the HOB list until end of list or matching type is found.\r
226 //\r
227 while (!END_OF_HOB_LIST (Hob)) {\r
228 if (Hob.Header->Reserved != (UINT32)0) {\r
229 DEBUG ((DEBUG_ERROR, "HOB: Hob header Reserved filed should be zero\n"));\r
230 return FALSE;\r
231 }\r
232\r
233 if (Hob.Header->HobLength == 0) {\r
234 DEBUG ((DEBUG_ERROR, "HOB: Hob header LEANGTH should not be zero\n"));\r
235 return FALSE;\r
236 }\r
237\r
238 switch (Hob.Header->HobType) {\r
239 case EFI_HOB_TYPE_HANDOFF:\r
240 if (Hob.Header->HobLength != sizeof (EFI_HOB_HANDOFF_INFO_TABLE)) {\r
241 DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_HANDOFF));\r
242 return FALSE;\r
243 }\r
244\r
245 if (IsInValidList (Hob.HandoffInformationTable->BootMode, EFI_BOOT_MODE_LIST, ARRAY_SIZE (EFI_BOOT_MODE_LIST)) == FALSE) {\r
246 DEBUG ((DEBUG_ERROR, "HOB: Unknow HandoffInformationTable BootMode type. Type: 0x%08x\n", Hob.HandoffInformationTable->BootMode));\r
247 return FALSE;\r
248 }\r
249\r
250 if ((Hob.HandoffInformationTable->EfiFreeMemoryTop % 4096) != 0) {\r
251 DEBUG ((DEBUG_ERROR, "HOB: HandoffInformationTable EfiFreeMemoryTop address must be 4-KB aligned to meet page restrictions of UEFI.\\r
252 Address: 0x%016lx\n", Hob.HandoffInformationTable->EfiFreeMemoryTop));\r
253 return FALSE;\r
254 }\r
255\r
256 break;\r
257\r
258 case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:\r
259 if (Hob.Header->HobLength != sizeof (EFI_HOB_RESOURCE_DESCRIPTOR)) {\r
260 DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_RESOURCE_DESCRIPTOR));\r
261 return FALSE;\r
262 }\r
263\r
264 if (IsInValidList (Hob.ResourceDescriptor->ResourceType, EFI_RESOURCE_TYPE_LIST, ARRAY_SIZE (EFI_RESOURCE_TYPE_LIST)) == FALSE) {\r
265 DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceType type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceType));\r
266 return FALSE;\r
267 }\r
268\r
269 if ((Hob.ResourceDescriptor->ResourceAttribute & (~(EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
270 EFI_RESOURCE_ATTRIBUTE_INITIALIZED |\r
271 EFI_RESOURCE_ATTRIBUTE_TESTED |\r
272 EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED |\r
273 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED |\r
274 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED |\r
275 EFI_RESOURCE_ATTRIBUTE_PERSISTENT |\r
276 EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC |\r
277 EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC |\r
278 EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1 |\r
279 EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2 |\r
280 EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |\r
281 EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |\r
282 EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |\r
283 EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |\r
284 EFI_RESOURCE_ATTRIBUTE_16_BIT_IO |\r
285 EFI_RESOURCE_ATTRIBUTE_32_BIT_IO |\r
286 EFI_RESOURCE_ATTRIBUTE_64_BIT_IO |\r
287 EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED |\r
288 EFI_RESOURCE_ATTRIBUTE_READ_PROTECTABLE |\r
289 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTABLE |\r
290 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTABLE |\r
291 EFI_RESOURCE_ATTRIBUTE_PERSISTABLE |\r
292 EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED |\r
293 EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE |\r
294 EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE))) != 0)\r
295 {\r
296 DEBUG ((DEBUG_ERROR, "HOB: Unknow ResourceDescriptor ResourceAttribute type. Type: 0x%08x\n", Hob.ResourceDescriptor->ResourceAttribute));\r
297 return FALSE;\r
298 }\r
299\r
300 break;\r
301\r
302 // EFI_HOB_GUID_TYPE is variable length data, so skip check\r
303 case EFI_HOB_TYPE_GUID_EXTENSION:\r
304 break;\r
305\r
306 case EFI_HOB_TYPE_FV:\r
307 if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME)) {\r
308 DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV));\r
309 return FALSE;\r
310 }\r
311\r
312 break;\r
313\r
314 case EFI_HOB_TYPE_FV2:\r
315 if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME2)) {\r
316 DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV2));\r
317 return FALSE;\r
318 }\r
319\r
320 break;\r
321\r
322 case EFI_HOB_TYPE_FV3:\r
323 if (Hob.Header->HobLength != sizeof (EFI_HOB_FIRMWARE_VOLUME3)) {\r
324 DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV3));\r
325 return FALSE;\r
326 }\r
327\r
328 break;\r
329\r
330 case EFI_HOB_TYPE_CPU:\r
331 if (Hob.Header->HobLength != sizeof (EFI_HOB_CPU)) {\r
332 DEBUG ((DEBUG_ERROR, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_CPU));\r
333 return FALSE;\r
334 }\r
335\r
336 for (UINT32 index = 0; index < 6; index++) {\r
337 if (Hob.Cpu->Reserved[index] != 0) {\r
338 DEBUG ((DEBUG_ERROR, "HOB: Cpu Reserved field will always be set to zero.\n"));\r
339 return FALSE;\r
340 }\r
341 }\r
342\r
343 break;\r
344\r
345 default:\r
346 DEBUG ((DEBUG_ERROR, "HOB: Hob type is not know. Type: 0x%04x\n", Hob.Header->HobType));\r
347 return FALSE;\r
348 }\r
349\r
350 // Get next HOB\r
351 Hob.Raw = (UINT8 *)(Hob.Raw + Hob.Header->HobLength);\r
352 }\r
353\r
354 return TRUE;\r
355}\r
356\r
357/**\r
358 Processing the incoming HobList for the TDX\r
359\r
360 Firmware must parse list, and accept the pages of memory before their can be\r
361 use by the guest.\r
362\r
363 @param[in] VmmHobList The Hoblist pass the firmware\r
364\r
365 @retval EFI_SUCCESS Process the HobList successfully\r
366 @retval Others Other errors as indicated\r
367\r
368**/\r
369EFI_STATUS\r
370EFIAPI\r
371ProcessHobList (\r
372 IN CONST VOID *VmmHobList\r
373 )\r
374{\r
375 EFI_STATUS Status;\r
376 EFI_PEI_HOB_POINTERS Hob;\r
377 EFI_PHYSICAL_ADDRESS PhysicalEnd;\r
378\r
379 Status = EFI_SUCCESS;\r
380 ASSERT (VmmHobList != NULL);\r
381 Hob.Raw = (UINT8 *)VmmHobList;\r
382\r
383 //\r
384 // Parse the HOB list until end of list or matching type is found.\r
385 //\r
386 while (!END_OF_HOB_LIST (Hob)) {\r
387 if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {\r
388 DEBUG ((DEBUG_INFO, "\nResourceType: 0x%x\n", Hob.ResourceDescriptor->ResourceType));\r
389\r
390 if (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_MEMORY_UNACCEPTED) {\r
391 DEBUG ((DEBUG_INFO, "ResourceAttribute: 0x%x\n", Hob.ResourceDescriptor->ResourceAttribute));\r
392 DEBUG ((DEBUG_INFO, "PhysicalStart: 0x%llx\n", Hob.ResourceDescriptor->PhysicalStart));\r
393 DEBUG ((DEBUG_INFO, "ResourceLength: 0x%llx\n", Hob.ResourceDescriptor->ResourceLength));\r
394 DEBUG ((DEBUG_INFO, "Owner: %g\n\n", &Hob.ResourceDescriptor->Owner));\r
395\r
396 PhysicalEnd = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength;\r
397\r
398 Status = BspAcceptMemoryResourceRange (\r
399 Hob.ResourceDescriptor->PhysicalStart,\r
400 PhysicalEnd\r
401 );\r
402 if (EFI_ERROR (Status)) {\r
403 break;\r
404 }\r
405 }\r
406 }\r
407\r
408 Hob.Raw = GET_NEXT_HOB (Hob);\r
409 }\r
410\r
411 return Status;\r
412}\r
413\r
414/**\r
415 In Tdx guest, some information need to be passed from host VMM to guest\r
416 firmware. For example, the memory resource, etc. These information are\r
417 prepared by host VMM and put in HobList which is described in TdxMetadata.\r
418\r
419 Information in HobList is treated as external input. From the security\r
420 perspective before it is consumed, it should be validated.\r
421\r
422 @retval EFI_SUCCESS Successfully process the hoblist\r
423 @retval Others Other error as indicated\r
424**/\r
425EFI_STATUS\r
426EFIAPI\r
427ProcessTdxHobList (\r
428 VOID\r
429 )\r
430{\r
431 EFI_STATUS Status;\r
432 VOID *TdHob;\r
433 TD_RETURN_DATA TdReturnData;\r
434\r
435 TdHob = (VOID *)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase);\r
436 Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);\r
437 if (EFI_ERROR (Status)) {\r
438 return Status;\r
439 }\r
440\r
441 DEBUG ((\r
442 DEBUG_INFO,\r
443 "Intel Tdx Started with (GPAW: %d, Cpus: %d)\n",\r
444 TdReturnData.TdInfo.Gpaw,\r
445 TdReturnData.TdInfo.NumVcpus\r
446 ));\r
447\r
448 //\r
449 // Validate HobList\r
450 //\r
451 if (ValidateHobList (TdHob) == FALSE) {\r
452 return EFI_INVALID_PARAMETER;\r
453 }\r
454\r
455 //\r
456 // Process Hoblist to accept memory\r
457 //\r
458 Status = ProcessHobList (TdHob);\r
459\r
460 return Status;\r
461}\r
462\r
463/**\r
464 Transfer the incoming HobList for the TD to the final HobList for Dxe.\r
465 The Hobs transferred in this function are ResourceDescriptor hob and\r
466 MemoryAllocation hob.\r
467\r
468 @param[in] VmmHobList The Hoblist pass the firmware\r
469\r
470**/\r
471VOID\r
472EFIAPI\r
473TransferTdxHobList (\r
474 VOID\r
475 )\r
476{\r
477 EFI_PEI_HOB_POINTERS Hob;\r
478 EFI_RESOURCE_TYPE ResourceType;\r
479 EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute;\r
480\r
481 //\r
482 // PcdOvmfSecGhcbBase is used as the TD_HOB in Tdx guest.\r
483 //\r
484 Hob.Raw = (UINT8 *)(UINTN)FixedPcdGet32 (PcdOvmfSecGhcbBase);\r
485 while (!END_OF_HOB_LIST (Hob)) {\r
486 switch (Hob.Header->HobType) {\r
487 case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR:\r
488 ResourceType = Hob.ResourceDescriptor->ResourceType;\r
489 ResourceAttribute = Hob.ResourceDescriptor->ResourceAttribute;\r
490\r
491 if (ResourceType == EFI_RESOURCE_MEMORY_UNACCEPTED) {\r
492 ResourceType = EFI_RESOURCE_SYSTEM_MEMORY;\r
493 ResourceAttribute |= (EFI_RESOURCE_ATTRIBUTE_PRESENT | EFI_RESOURCE_ATTRIBUTE_INITIALIZED | EFI_RESOURCE_ATTRIBUTE_TESTED);\r
494 }\r
495\r
496 BuildResourceDescriptorHob (\r
497 ResourceType,\r
498 ResourceAttribute,\r
499 Hob.ResourceDescriptor->PhysicalStart,\r
500 Hob.ResourceDescriptor->ResourceLength\r
501 );\r
502 break;\r
503 case EFI_HOB_TYPE_MEMORY_ALLOCATION:\r
504 BuildMemoryAllocationHob (\r
505 Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress,\r
506 Hob.MemoryAllocation->AllocDescriptor.MemoryLength,\r
507 Hob.MemoryAllocation->AllocDescriptor.MemoryType\r
508 );\r
509 break;\r
510 }\r
511\r
512 Hob.Raw = GET_NEXT_HOB (Hob);\r
513 }\r
514}\r