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