RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429 TdxLib is created with functions to perform the related Tdx operation. This includes functions for: - TdAcceptPages : Accept pending private pages and initialize the pages to all-0 using the TD ephemeral private key. - TdExtendRtmr : Extend measurement to one of the RTMR registers. - TdSharedPageMask: Get the Td guest shared page mask which indicates it is a Shared or Private page. - TdMaxVCpuNum : Get the maximum number of virtual CPUs. - TdVCpuNum : Get the number of virtual CPUs. Cc: Michael D Kinney <michael.d.kinney@intel.com> Cc: Liming Gao <gaoliming@byosoft.com.cn> Cc: Zhiguang Liu <zhiguang.liu@intel.com> Cc: Brijesh Singh <brijesh.singh@amd.com> Cc: Erdem Aktas <erdemaktas@google.com> Cc: James Bottomley <jejb@linux.ibm.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Tom Lendacky <thomas.lendacky@amd.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn> Reviewed-by: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Min Xu <min.m.xu@intel.com>
		
			
				
	
	
		
			182 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|   Unaccepted memory is a special type of private memory. In Td guest
 | |
|   TDCALL [TDG.MEM.PAGE.ACCEPT] is invoked to accept the unaccepted
 | |
|   memory before use it.
 | |
| 
 | |
|   Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include <Library/BaseLib.h>
 | |
| #include <Library/DebugLib.h>
 | |
| #include <IndustryStandard/Tdx.h>
 | |
| #include <Uefi/UefiBaseType.h>
 | |
| #include <Library/TdxLib.h>
 | |
| #include <Library/BaseMemoryLib.h>
 | |
| 
 | |
| UINT64  mNumberOfDuplicatedAcceptedPages;
 | |
| 
 | |
| #define TDX_ACCEPTPAGE_MAX_RETRIED  3
 | |
| 
 | |
| // PageSize is mapped to PageLevel like below:
 | |
| // 4KB - 0, 2MB - 1
 | |
| UINT32  mTdxAcceptPageLevelMap[2] = {
 | |
|   SIZE_4KB,
 | |
|   SIZE_2MB
 | |
| };
 | |
| 
 | |
| #define INVALID_ACCEPT_PAGELEVEL  ARRAY_SIZE(mTdxAcceptPageLevelMap)
 | |
| 
 | |
| /**
 | |
|   This function gets the PageLevel according to the input page size.
 | |
| 
 | |
|   @param[in]  PageSize    Page size
 | |
| 
 | |
|   @return UINT32          The mapped page level
 | |
| **/
 | |
| UINT32
 | |
| GetGpaPageLevel (
 | |
|   UINT32  PageSize
 | |
|   )
 | |
| {
 | |
|   UINT32  Index;
 | |
| 
 | |
|   for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++) {
 | |
|     if (mTdxAcceptPageLevelMap[Index] == PageSize) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return Index;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function accept a pending private page, and initialize the page to
 | |
|   all-0 using the TD ephemeral private key.
 | |
| 
 | |
|   Sometimes TDCALL [TDG.MEM.PAGE.ACCEPT] may return
 | |
|   TDX_EXIT_REASON_PAGE_SIZE_MISMATCH. It indicates the input PageLevel is
 | |
|   not workable. In this case we need to try to fallback to a smaller
 | |
|   PageLevel if possible.
 | |
| 
 | |
|   @param[in]  StartAddress      Guest physical address of the private
 | |
|                                 page to accept. [63:52] and [11:0] must be 0.
 | |
|   @param[in]  NumberOfPages     Number of the pages to be accepted.
 | |
|   @param[in]  PageSize          GPA page size. Only accept 2M/4K size.
 | |
| 
 | |
|   @return EFI_SUCCESS           Accept successfully
 | |
|   @return others                Indicate other errors
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| TdAcceptPages (
 | |
|   IN UINT64  StartAddress,
 | |
|   IN UINT64  NumberOfPages,
 | |
|   IN UINT32  PageSize
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT64      Address;
 | |
|   UINT64      TdxStatus;
 | |
|   UINT64      Index;
 | |
|   UINT32      GpaPageLevel;
 | |
|   UINT32      PageSize2;
 | |
|   UINTN       Retried;
 | |
| 
 | |
|   Retried = 0;
 | |
| 
 | |
|   if ((StartAddress & ~0xFFFFFFFFFF000ULL) != 0) {
 | |
|     ASSERT (FALSE);
 | |
|     DEBUG ((DEBUG_ERROR, "Accept page address(0x%llx) is not valid. [63:52] and [11:0] must be 0\n", StartAddress));
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Address = StartAddress;
 | |
| 
 | |
|   GpaPageLevel = GetGpaPageLevel (PageSize);
 | |
|   if (GpaPageLevel == INVALID_ACCEPT_PAGELEVEL) {
 | |
|     ASSERT (FALSE);
 | |
|     DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page size - 0x%llx\n", PageSize));
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Status = EFI_SUCCESS;
 | |
|   for (Index = 0; Index < NumberOfPages; Index++) {
 | |
|     Retried = 0;
 | |
| 
 | |
| DoAcceptPage:
 | |
|     TdxStatus = TdCall (TDCALL_TDACCEPTPAGE, Address | GpaPageLevel, 0, 0, 0);
 | |
|     if (TdxStatus != TDX_EXIT_REASON_SUCCESS) {
 | |
|       if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) {
 | |
|         //
 | |
|         // Already accepted
 | |
|         //
 | |
|         mNumberOfDuplicatedAcceptedPages++;
 | |
|         DEBUG ((DEBUG_WARN, "Page at Address (0x%llx) has already been accepted. - %d\n", Address, mNumberOfDuplicatedAcceptedPages));
 | |
|       } else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) {
 | |
|         //
 | |
|         // GpaPageLevel is mismatch, fall back to a smaller GpaPageLevel if possible
 | |
|         //
 | |
|         DEBUG ((DEBUG_VERBOSE, "Address %llx cannot be accepted in PageLevel of %d\n", Address, GpaPageLevel));
 | |
| 
 | |
|         if (GpaPageLevel == 0) {
 | |
|           //
 | |
|           // Cannot fall back to smaller page level
 | |
|           //
 | |
|           DEBUG ((DEBUG_ERROR, "AcceptPage cannot fallback from PageLevel %d\n", GpaPageLevel));
 | |
|           Status = EFI_INVALID_PARAMETER;
 | |
|           break;
 | |
|         } else {
 | |
|           //
 | |
|           // Fall back to a smaller page size
 | |
|           //
 | |
|           PageSize2 = mTdxAcceptPageLevelMap[GpaPageLevel - 1];
 | |
|           Status    = TdAcceptPages (Address, 512, PageSize2);
 | |
|           if (EFI_ERROR (Status)) {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|       } else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_OPERAND_BUSY) {
 | |
|         //
 | |
|         // Concurrent TDG.MEM.PAGE.ACCEPT is using the same Secure EPT entry
 | |
|         // So try it again. There is a max retried count. If Retried exceeds the max count,
 | |
|         // report the error and quit.
 | |
|         //
 | |
|         Retried += 1;
 | |
|         if (Retried > TDX_ACCEPTPAGE_MAX_RETRIED) {
 | |
|           DEBUG ((
 | |
|             DEBUG_ERROR,
 | |
|             "Address %llx (%d) failed to be accepted because of OPERAND_BUSY. Retried %d time.\n",
 | |
|             Address,
 | |
|             Index,
 | |
|             Retried
 | |
|             ));
 | |
|           Status = EFI_INVALID_PARAMETER;
 | |
|           break;
 | |
|         } else {
 | |
|           goto DoAcceptPage;
 | |
|         }
 | |
|       } else {
 | |
|         //
 | |
|         // Other errors
 | |
|         //
 | |
|         DEBUG ((
 | |
|           DEBUG_ERROR,
 | |
|           "Address %llx (%d) failed to be accepted. Error = 0x%llx\n",
 | |
|           Address,
 | |
|           Index,
 | |
|           TdxStatus
 | |
|           ));
 | |
|         Status = EFI_INVALID_PARAMETER;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Address += PageSize;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 |