2 Initialize Intel TDX support.
4 Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include <Library/BaseLib.h>
13 #include <Library/DebugLib.h>
14 #include <Library/HobLib.h>
15 #include <Library/BaseMemoryLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <IndustryStandard/Tdx.h>
18 #include <IndustryStandard/IntelTdx.h>
19 #include <IndustryStandard/QemuFwCfg.h>
20 #include <Library/QemuFwCfgLib.h>
21 #include <Library/PeiServicesLib.h>
22 #include <Library/TdxLib.h>
23 #include <Library/SynchronizationLib.h>
24 #include <Pi/PrePiHob.h>
26 #include <ConfidentialComputingGuestAttr.h>
28 #define ALIGNED_2MB_MASK 0x1fffff
29 #define MEGABYTE_SHIFT 20
32 This function will be called to accept pages. Only BSP accepts pages.
34 TDCALL(ACCEPT_PAGE) supports the accept page size of 4k and 2M. To
35 simplify the implementation, the Memory to be accpeted is splitted
37 ----------------- <-- StartAddress1 (not 2M aligned)
38 | part 1 | Length1 < 2M
39 |---------------| <-- StartAddress2 (2M aligned)
40 | | Length2 = Integer multiples of 2M
43 |---------------| <-- StartAddress3
44 | part 3 | Length3 < 2M
47 @param[in] PhysicalAddress Start physical adress
48 @param[in] PhysicalEnd End physical address
50 @retval EFI_SUCCESS Accept memory successfully
51 @retval Others Other errors as indicated
55 BspAcceptMemoryResourceRange (
56 IN EFI_PHYSICAL_ADDRESS PhysicalAddress
,
57 IN EFI_PHYSICAL_ADDRESS PhysicalEnd
61 UINT32 AcceptPageSize
;
71 AcceptPageSize
= FixedPcdGet32 (PcdTdxAcceptPageSize
);
72 TotalLength
= PhysicalEnd
- PhysicalAddress
;
80 if (TotalLength
== 0) {
84 DEBUG ((DEBUG_INFO
, "TdAccept: 0x%llx - 0x%llx\n", PhysicalAddress
, TotalLength
));
86 if (ALIGN_VALUE (PhysicalAddress
, SIZE_2MB
) != PhysicalAddress
) {
87 StartAddress1
= PhysicalAddress
;
88 Length1
= ALIGN_VALUE (PhysicalAddress
, SIZE_2MB
) - PhysicalAddress
;
89 if (Length1
>= TotalLength
) {
90 Length1
= TotalLength
;
93 PhysicalAddress
+= Length1
;
94 TotalLength
-= Length1
;
97 if (TotalLength
> SIZE_2MB
) {
98 StartAddress2
= PhysicalAddress
;
99 Length2
= TotalLength
& ~(UINT64
)ALIGNED_2MB_MASK
;
100 PhysicalAddress
+= Length2
;
101 TotalLength
-= Length2
;
105 StartAddress3
= PhysicalAddress
;
106 Length3
= TotalLength
;
109 DEBUG ((DEBUG_INFO
, " Part1: 0x%llx - 0x%llx\n", StartAddress1
, Length1
));
110 DEBUG ((DEBUG_INFO
, " Part2: 0x%llx - 0x%llx\n", StartAddress2
, Length2
));
111 DEBUG ((DEBUG_INFO
, " Part3: 0x%llx - 0x%llx\n", StartAddress3
, Length3
));
112 DEBUG ((DEBUG_INFO
, " Page : 0x%x\n", AcceptPageSize
));
114 Status
= EFI_SUCCESS
;
116 Pages
= Length1
/ SIZE_4KB
;
117 Status
= TdAcceptPages (StartAddress1
, Pages
, SIZE_4KB
);
118 if (EFI_ERROR (Status
)) {
124 Pages
= Length2
/ AcceptPageSize
;
125 Status
= TdAcceptPages (StartAddress2
, Pages
, AcceptPageSize
);
126 if (EFI_ERROR (Status
)) {
132 Pages
= Length3
/ SIZE_4KB
;
133 Status
= TdAcceptPages (StartAddress3
, Pages
, SIZE_4KB
);
134 ASSERT (!EFI_ERROR (Status
));
135 if (EFI_ERROR (Status
)) {
144 Check the value whether in the valid list.
146 @param[in] Value A value
147 @param[in] ValidList A pointer to valid list
148 @param[in] ValidListLength Length of valid list
150 @retval TRUE The value is in valid list.
151 @retval FALSE The value is not in valid list.
158 IN UINT32
*ValidList
,
159 IN UINT32 ValidListLength
164 if (ValidList
== NULL
) {
168 for (index
= 0; index
< ValidListLength
; index
++) {
169 if (ValidList
[index
] == Value
) {
178 Check the integrity of VMM Hob List.
180 @param[in] VmmHobList A pointer to Hob List
182 @retval TRUE The Hob List is valid.
183 @retval FALSE The Hob List is invalid.
189 IN CONST VOID
*VmmHobList
192 EFI_PEI_HOB_POINTERS Hob
;
193 UINT32 EFI_BOOT_MODE_LIST
[] = {
194 BOOT_WITH_FULL_CONFIGURATION
,
195 BOOT_WITH_MINIMAL_CONFIGURATION
,
196 BOOT_ASSUMING_NO_CONFIGURATION_CHANGES
,
197 BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS
,
198 BOOT_WITH_DEFAULT_SETTINGS
,
201 BOOT_WITH_MFG_MODE_SETTINGS
,
204 BOOT_ON_FLASH_UPDATE
,
205 BOOT_IN_RECOVERY_MODE
208 UINT32 EFI_RESOURCE_TYPE_LIST
[] = {
209 EFI_RESOURCE_SYSTEM_MEMORY
,
210 EFI_RESOURCE_MEMORY_MAPPED_IO
,
212 EFI_RESOURCE_FIRMWARE_DEVICE
,
213 EFI_RESOURCE_MEMORY_MAPPED_IO_PORT
,
214 EFI_RESOURCE_MEMORY_RESERVED
,
215 EFI_RESOURCE_IO_RESERVED
,
216 BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED
219 if (VmmHobList
== NULL
) {
220 DEBUG ((DEBUG_ERROR
, "HOB: HOB data pointer is NULL\n"));
224 Hob
.Raw
= (UINT8
*)VmmHobList
;
227 // Parse the HOB list until end of list or matching type is found.
229 while (!END_OF_HOB_LIST (Hob
)) {
230 if (Hob
.Header
->Reserved
!= (UINT32
)0) {
231 DEBUG ((DEBUG_ERROR
, "HOB: Hob header Reserved filed should be zero\n"));
235 if (Hob
.Header
->HobLength
== 0) {
236 DEBUG ((DEBUG_ERROR
, "HOB: Hob header LEANGTH should not be zero\n"));
240 switch (Hob
.Header
->HobType
) {
241 case EFI_HOB_TYPE_HANDOFF
:
242 if (Hob
.Header
->HobLength
!= sizeof (EFI_HOB_HANDOFF_INFO_TABLE
)) {
243 DEBUG ((DEBUG_ERROR
, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_HANDOFF
));
247 if (IsInValidList (Hob
.HandoffInformationTable
->BootMode
, EFI_BOOT_MODE_LIST
, ARRAY_SIZE (EFI_BOOT_MODE_LIST
)) == FALSE
) {
248 DEBUG ((DEBUG_ERROR
, "HOB: Unknow HandoffInformationTable BootMode type. Type: 0x%08x\n", Hob
.HandoffInformationTable
->BootMode
));
252 if ((Hob
.HandoffInformationTable
->EfiFreeMemoryTop
% 4096) != 0) {
253 DEBUG ((DEBUG_ERROR
, "HOB: HandoffInformationTable EfiFreeMemoryTop address must be 4-KB aligned to meet page restrictions of UEFI.\
254 Address: 0x%016lx\n", Hob
.HandoffInformationTable
->EfiFreeMemoryTop
));
260 case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
:
261 if (Hob
.Header
->HobLength
!= sizeof (EFI_HOB_RESOURCE_DESCRIPTOR
)) {
262 DEBUG ((DEBUG_ERROR
, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
));
266 if (IsInValidList (Hob
.ResourceDescriptor
->ResourceType
, EFI_RESOURCE_TYPE_LIST
, ARRAY_SIZE (EFI_RESOURCE_TYPE_LIST
)) == FALSE
) {
267 DEBUG ((DEBUG_ERROR
, "HOB: Unknow ResourceDescriptor ResourceType type. Type: 0x%08x\n", Hob
.ResourceDescriptor
->ResourceType
));
271 if ((Hob
.ResourceDescriptor
->ResourceAttribute
& (~(EFI_RESOURCE_ATTRIBUTE_PRESENT
|
272 EFI_RESOURCE_ATTRIBUTE_INITIALIZED
|
273 EFI_RESOURCE_ATTRIBUTE_TESTED
|
274 EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED
|
275 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED
|
276 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED
|
277 EFI_RESOURCE_ATTRIBUTE_PERSISTENT
|
278 EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC
|
279 EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC
|
280 EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1
|
281 EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2
|
282 EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE
|
283 EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE
|
284 EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE
|
285 EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE
|
286 EFI_RESOURCE_ATTRIBUTE_16_BIT_IO
|
287 EFI_RESOURCE_ATTRIBUTE_32_BIT_IO
|
288 EFI_RESOURCE_ATTRIBUTE_64_BIT_IO
|
289 EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED
|
290 EFI_RESOURCE_ATTRIBUTE_READ_PROTECTABLE
|
291 EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTABLE
|
292 EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTABLE
|
293 EFI_RESOURCE_ATTRIBUTE_PERSISTABLE
|
294 EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED
|
295 EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE
|
296 EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE
))) != 0)
298 DEBUG ((DEBUG_ERROR
, "HOB: Unknow ResourceDescriptor ResourceAttribute type. Type: 0x%08x\n", Hob
.ResourceDescriptor
->ResourceAttribute
));
304 // EFI_HOB_GUID_TYPE is variable length data, so skip check
305 case EFI_HOB_TYPE_GUID_EXTENSION
:
308 case EFI_HOB_TYPE_FV
:
309 if (Hob
.Header
->HobLength
!= sizeof (EFI_HOB_FIRMWARE_VOLUME
)) {
310 DEBUG ((DEBUG_ERROR
, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV
));
316 case EFI_HOB_TYPE_FV2
:
317 if (Hob
.Header
->HobLength
!= sizeof (EFI_HOB_FIRMWARE_VOLUME2
)) {
318 DEBUG ((DEBUG_ERROR
, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV2
));
324 case EFI_HOB_TYPE_FV3
:
325 if (Hob
.Header
->HobLength
!= sizeof (EFI_HOB_FIRMWARE_VOLUME3
)) {
326 DEBUG ((DEBUG_ERROR
, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_FV3
));
332 case EFI_HOB_TYPE_CPU
:
333 if (Hob
.Header
->HobLength
!= sizeof (EFI_HOB_CPU
)) {
334 DEBUG ((DEBUG_ERROR
, "HOB: Hob length is not equal corresponding hob structure. Type: 0x%04x\n", EFI_HOB_TYPE_CPU
));
338 for (UINT32 index
= 0; index
< 6; index
++) {
339 if (Hob
.Cpu
->Reserved
[index
] != 0) {
340 DEBUG ((DEBUG_ERROR
, "HOB: Cpu Reserved field will always be set to zero.\n"));
348 DEBUG ((DEBUG_ERROR
, "HOB: Hob type is not know. Type: 0x%04x\n", Hob
.Header
->HobType
));
353 Hob
.Raw
= (UINT8
*)(Hob
.Raw
+ Hob
.Header
->HobLength
);
360 Processing the incoming HobList for the TDX
362 Firmware must parse list, and accept the pages of memory before their can be
365 @param[in] VmmHobList The Hoblist pass the firmware
367 @retval EFI_SUCCESS Process the HobList successfully
368 @retval Others Other errors as indicated
374 IN CONST VOID
*VmmHobList
378 EFI_PEI_HOB_POINTERS Hob
;
379 EFI_PHYSICAL_ADDRESS PhysicalEnd
;
380 UINT64 ResourceLength
;
381 UINT64 AccumulateAcceptedMemory
;
383 Status
= EFI_SUCCESS
;
384 ASSERT (VmmHobList
!= NULL
);
385 Hob
.Raw
= (UINT8
*)VmmHobList
;
387 AccumulateAcceptedMemory
= 0;
390 // Parse the HOB list until end of list or matching type is found.
392 while (!END_OF_HOB_LIST (Hob
)) {
393 if (Hob
.Header
->HobType
== EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
) {
394 DEBUG ((DEBUG_INFO
, "\nResourceType: 0x%x\n", Hob
.ResourceDescriptor
->ResourceType
));
396 if (Hob
.ResourceDescriptor
->ResourceType
== BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED
) {
397 DEBUG ((DEBUG_INFO
, "ResourceAttribute: 0x%x\n", Hob
.ResourceDescriptor
->ResourceAttribute
));
398 DEBUG ((DEBUG_INFO
, "PhysicalStart: 0x%llx\n", Hob
.ResourceDescriptor
->PhysicalStart
));
399 DEBUG ((DEBUG_INFO
, "ResourceLength: 0x%llx\n", Hob
.ResourceDescriptor
->ResourceLength
));
400 DEBUG ((DEBUG_INFO
, "Owner: %g\n\n", &Hob
.ResourceDescriptor
->Owner
));
402 PhysicalEnd
= Hob
.ResourceDescriptor
->PhysicalStart
+ Hob
.ResourceDescriptor
->ResourceLength
;
403 ResourceLength
= Hob
.ResourceDescriptor
->ResourceLength
;
405 if (Hob
.ResourceDescriptor
->PhysicalStart
>= BASE_4GB
) {
407 // In current stage, we only accept the memory under 4G
412 Status
= BspAcceptMemoryResourceRange (
413 Hob
.ResourceDescriptor
->PhysicalStart
,
416 if (EFI_ERROR (Status
)) {
420 AccumulateAcceptedMemory
+= ResourceLength
;
424 Hob
.Raw
= GET_NEXT_HOB (Hob
);
431 In Tdx guest, some information need to be passed from host VMM to guest
432 firmware. For example, the memory resource, etc. These information are
433 prepared by host VMM and put in HobList which is described in TdxMetadata.
435 Information in HobList is treated as external input. From the security
436 perspective before it is consumed, it should be validated.
438 @retval EFI_SUCCESS Successfully process the hoblist
439 @retval Others Other error as indicated
449 TD_RETURN_DATA TdReturnData
;
451 TdHob
= (VOID
*)(UINTN
)FixedPcdGet32 (PcdOvmfSecGhcbBase
);
452 Status
= TdCall (TDCALL_TDINFO
, 0, 0, 0, &TdReturnData
);
453 if (EFI_ERROR (Status
)) {
459 "Intel Tdx Started with (GPAW: %d, Cpus: %d)\n",
460 TdReturnData
.TdInfo
.Gpaw
,
461 TdReturnData
.TdInfo
.NumVcpus
467 if (ValidateHobList (TdHob
) == FALSE
) {
468 return EFI_INVALID_PARAMETER
;
472 // Process Hoblist to accept memory
474 Status
= ProcessHobList (TdHob
);
480 * Build ResourceDescriptorHob for the unaccepted memory region.
481 * This memory region may be splitted into 2 parts because of lazy accept.
483 * @param Hob Point to the EFI_HOB_RESOURCE_DESCRIPTOR
487 BuildResourceDescriptorHobForUnacceptedMemory (
488 IN EFI_HOB_RESOURCE_DESCRIPTOR
*Hob
491 EFI_PHYSICAL_ADDRESS PhysicalStart
;
492 EFI_PHYSICAL_ADDRESS PhysicalEnd
;
493 UINT64 ResourceLength
;
494 EFI_RESOURCE_TYPE ResourceType
;
495 EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute
;
496 UINT64 MaxAcceptedMemoryAddress
;
498 ASSERT (Hob
->ResourceType
== BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED
);
500 ResourceType
= BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED
;
501 ResourceAttribute
= Hob
->ResourceAttribute
;
502 PhysicalStart
= Hob
->PhysicalStart
;
503 ResourceLength
= Hob
->ResourceLength
;
504 PhysicalEnd
= PhysicalStart
+ ResourceLength
;
507 // In the first stage of lazy-accept, all the memory under 4G will be accepted.
508 // The memory above 4G will not be accepted.
510 MaxAcceptedMemoryAddress
= BASE_4GB
;
512 if (PhysicalEnd
<= MaxAcceptedMemoryAddress
) {
514 // This memory region has been accepted.
516 ResourceType
= EFI_RESOURCE_SYSTEM_MEMORY
;
517 ResourceAttribute
|= (EFI_RESOURCE_ATTRIBUTE_PRESENT
| EFI_RESOURCE_ATTRIBUTE_INITIALIZED
| EFI_RESOURCE_ATTRIBUTE_TESTED
);
518 } else if (PhysicalStart
>= MaxAcceptedMemoryAddress
) {
520 // This memory region hasn't been accepted.
521 // So keep the ResourceType and ResourceAttribute unchange.
525 BuildResourceDescriptorHob (
534 Transfer the incoming HobList for the TD to the final HobList for Dxe.
535 The Hobs transferred in this function are ResourceDescriptor hob and
536 MemoryAllocation hob.
538 @param[in] VmmHobList The Hoblist pass the firmware
547 EFI_PEI_HOB_POINTERS Hob
;
548 EFI_RESOURCE_TYPE ResourceType
;
549 EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute
;
552 // PcdOvmfSecGhcbBase is used as the TD_HOB in Tdx guest.
554 Hob
.Raw
= (UINT8
*)(UINTN
)FixedPcdGet32 (PcdOvmfSecGhcbBase
);
555 while (!END_OF_HOB_LIST (Hob
)) {
556 switch (Hob
.Header
->HobType
) {
557 case EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
:
558 ResourceType
= Hob
.ResourceDescriptor
->ResourceType
;
559 ResourceAttribute
= Hob
.ResourceDescriptor
->ResourceAttribute
;
561 if (ResourceType
== BZ3937_EFI_RESOURCE_MEMORY_UNACCEPTED
) {
562 BuildResourceDescriptorHobForUnacceptedMemory (Hob
.ResourceDescriptor
);
564 BuildResourceDescriptorHob (
567 Hob
.ResourceDescriptor
->PhysicalStart
,
568 Hob
.ResourceDescriptor
->ResourceLength
573 case EFI_HOB_TYPE_MEMORY_ALLOCATION
:
574 BuildMemoryAllocationHob (
575 Hob
.MemoryAllocation
->AllocDescriptor
.MemoryBaseAddress
,
576 Hob
.MemoryAllocation
->AllocDescriptor
.MemoryLength
,
577 Hob
.MemoryAllocation
->AllocDescriptor
.MemoryType
582 Hob
.Raw
= GET_NEXT_HOB (Hob
);
587 In Tdx guest, the system memory is passed in TdHob by host VMM. So
588 the major task of PlatformTdxPublishRamRegions is to walk thru the
589 TdHob list and transfer the ResourceDescriptorHob and MemoryAllocationHob
590 to the hobs in DXE phase.
592 MemoryAllocationHob should also be created for Mailbox and Ovmf work area.
596 PlatformTdxPublishRamRegions (
600 if (!TdIsEnabled ()) {
604 TransferTdxHobList ();
607 // The memory region defined by PcdOvmfSecGhcbBackupBase is pre-allocated by
608 // host VMM and used as the td mailbox at the beginning of system boot.
610 BuildMemoryAllocationHob (
611 FixedPcdGet32 (PcdOvmfSecGhcbBackupBase
),
612 FixedPcdGet32 (PcdOvmfSecGhcbBackupSize
),
616 if (FixedPcdGet32 (PcdOvmfWorkAreaSize
) != 0) {
618 // Reserve the work area.
620 // Since this memory range will be used by the Reset Vector on S3
621 // resume, it must be reserved as ACPI NVS.
623 // If S3 is unsupported, then various drivers might still write to the
624 // work area. We ought to prevent DXE from serving allocation requests
625 // such that they would overlap the work area.
627 BuildMemoryAllocationHob (
628 (EFI_PHYSICAL_ADDRESS
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaBase
),
629 (UINT64
)(UINTN
)FixedPcdGet32 (PcdOvmfWorkAreaSize
),