/** @file | |
TDX Dxe driver. This driver is dispatched early in DXE, due to being list | |
in APRIORI. | |
This module is responsible for: | |
- Sets max logical cpus based on TDINFO | |
- Sets PCI PCDs based on resource hobs | |
- Alter MATD table to record address of Mailbox | |
Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/DxeServicesTableLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/PcdLib.h> | |
#include <Library/UefiLib.h> | |
#include <Library/HobLib.h> | |
#include <Protocol/Cpu.h> | |
#include <Protocol/MpInitLibDepProtocols.h> | |
#include <Protocol/MemoryAccept.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <ConfidentialComputingGuestAttr.h> | |
#include <IndustryStandard/Tdx.h> | |
#include <Library/PlatformInitLib.h> | |
#include <Library/TdxLib.h> | |
#include <TdxAcpiTable.h> | |
#include <Library/MemEncryptTdxLib.h> | |
#define ALIGNED_2MB_MASK 0x1fffff | |
EFI_HANDLE mTdxDxeHandle = NULL; | |
EFI_STATUS | |
EFIAPI | |
TdxMemoryAccept ( | |
IN EDKII_MEMORY_ACCEPT_PROTOCOL *This, | |
IN EFI_PHYSICAL_ADDRESS StartAddress, | |
IN UINTN Size | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 AcceptPageSize; | |
UINT64 StartAddress1; | |
UINT64 StartAddress2; | |
UINT64 StartAddress3; | |
UINT64 Length1; | |
UINT64 Length2; | |
UINT64 Length3; | |
UINT64 Pages; | |
AcceptPageSize = FixedPcdGet32 (PcdTdxAcceptPageSize); | |
StartAddress1 = 0; | |
StartAddress2 = 0; | |
StartAddress3 = 0; | |
Length1 = 0; | |
Length2 = 0; | |
Length3 = 0; | |
if (Size == 0) { | |
return EFI_SUCCESS; | |
} | |
if (ALIGN_VALUE (StartAddress, SIZE_2MB) != StartAddress) { | |
StartAddress1 = StartAddress; | |
Length1 = ALIGN_VALUE (StartAddress, SIZE_2MB) - StartAddress; | |
if (Length1 >= Size) { | |
Length1 = Size; | |
} | |
StartAddress += Length1; | |
Size -= Length1; | |
} | |
if (Size > SIZE_2MB) { | |
StartAddress2 = StartAddress; | |
Length2 = Size & ~(UINT64)ALIGNED_2MB_MASK; | |
StartAddress += Length2; | |
Size -= Length2; | |
} | |
if (Size) { | |
StartAddress3 = StartAddress; | |
Length3 = Size; | |
} | |
Status = EFI_SUCCESS; | |
if (Length1 > 0) { | |
Pages = Length1 / SIZE_4KB; | |
Status = TdAcceptPages (StartAddress1, Pages, SIZE_4KB); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
if (Length2 > 0) { | |
Pages = Length2 / AcceptPageSize; | |
Status = TdAcceptPages (StartAddress2, Pages, AcceptPageSize); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
if (Length3 > 0) { | |
Pages = Length3 / SIZE_4KB; | |
Status = TdAcceptPages (StartAddress3, Pages, SIZE_4KB); | |
ASSERT (!EFI_ERROR (Status)); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
return Status; | |
} | |
EDKII_MEMORY_ACCEPT_PROTOCOL mMemoryAcceptProtocol = { | |
TdxMemoryAccept | |
}; | |
VOID | |
SetPcdSettings ( | |
EFI_HOB_PLATFORM_INFO *PlatformInfoHob | |
) | |
{ | |
RETURN_STATUS PcdStatus; | |
PcdStatus = PcdSet64S (PcdConfidentialComputingGuestAttr, PlatformInfoHob->PcdConfidentialComputingGuestAttr); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
PcdStatus = PcdSetBoolS (PcdSetNxForStack, PlatformInfoHob->PcdSetNxForStack); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
DEBUG (( | |
DEBUG_INFO, | |
"HostBridgeDevId=0x%x, CCAttr=0x%x, SetNxForStack=%x\n", | |
PlatformInfoHob->HostBridgeDevId, | |
PlatformInfoHob->PcdConfidentialComputingGuestAttr, | |
PlatformInfoHob->PcdSetNxForStack | |
)); | |
PcdStatus = PcdSet32S (PcdCpuBootLogicalProcessorNumber, PlatformInfoHob->PcdCpuBootLogicalProcessorNumber); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
PcdStatus = PcdSet32S (PcdCpuMaxLogicalProcessorNumber, PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
DEBUG (( | |
DEBUG_INFO, | |
"MaxCpuCount=0x%x, BootCpuCount=0x%x\n", | |
PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber, | |
PlatformInfoHob->PcdCpuBootLogicalProcessorNumber | |
)); | |
PcdSet64S (PcdEmuVariableNvStoreReserved, PlatformInfoHob->PcdEmuVariableNvStoreReserved); | |
if (TdIsEnabled ()) { | |
PcdStatus = PcdSet64S (PcdTdxSharedBitMask, TdSharedPageMask ()); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
DEBUG ((DEBUG_INFO, "TdxSharedBitMask=0x%llx\n", PcdGet64 (PcdTdxSharedBitMask))); | |
} | |
PcdStatus = PcdSet64S (PcdPciMmio64Base, PlatformInfoHob->PcdPciMmio64Base); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
PcdStatus = PcdSet64S (PcdPciMmio64Size, PlatformInfoHob->PcdPciMmio64Size); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
PcdStatus = PcdSet64S (PcdPciMmio32Base, PlatformInfoHob->PcdPciMmio32Base); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
PcdStatus = PcdSet64S (PcdPciMmio32Size, PlatformInfoHob->PcdPciMmio32Size); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
PcdStatus = PcdSet64S (PcdPciIoBase, PlatformInfoHob->PcdPciIoBase); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
PcdStatus = PcdSet64S (PcdPciIoSize, PlatformInfoHob->PcdPciIoSize); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
} | |
/** | |
Location of resource hob matching type and starting address | |
@param[in] Type The type of resource hob to locate. | |
@param[in] Start The resource hob must at least begin at address. | |
@retval pointer to resource Return pointer to a resource hob that matches or NULL. | |
**/ | |
STATIC | |
EFI_HOB_RESOURCE_DESCRIPTOR * | |
GetResourceDescriptor ( | |
EFI_RESOURCE_TYPE Type, | |
EFI_PHYSICAL_ADDRESS Start, | |
EFI_PHYSICAL_ADDRESS End | |
) | |
{ | |
EFI_PEI_HOB_POINTERS Hob; | |
EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor = NULL; | |
Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR); | |
while (Hob.Raw != NULL) { | |
DEBUG (( | |
DEBUG_INFO, | |
"%a:%d: resource type 0x%x %llx %llx\n", | |
__func__, | |
__LINE__, | |
Hob.ResourceDescriptor->ResourceType, | |
Hob.ResourceDescriptor->PhysicalStart, | |
Hob.ResourceDescriptor->ResourceLength | |
)); | |
if ((Hob.ResourceDescriptor->ResourceType == Type) && | |
(Hob.ResourceDescriptor->PhysicalStart >= Start) && | |
((Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength) < End)) | |
{ | |
ResourceDescriptor = Hob.ResourceDescriptor; | |
break; | |
} | |
Hob.Raw = GET_NEXT_HOB (Hob); | |
Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw); | |
} | |
return ResourceDescriptor; | |
} | |
/** | |
Location of resource hob matching type and highest address below end | |
@param[in] Type The type of resource hob to locate. | |
@param[in] End The resource hob return is the closest to the End address | |
@retval pointer to resource Return pointer to a resource hob that matches or NULL. | |
**/ | |
STATIC | |
EFI_HOB_RESOURCE_DESCRIPTOR * | |
GetHighestResourceDescriptor ( | |
EFI_RESOURCE_TYPE Type, | |
EFI_PHYSICAL_ADDRESS End | |
) | |
{ | |
EFI_PEI_HOB_POINTERS Hob; | |
EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor = NULL; | |
Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR); | |
while (Hob.Raw != NULL) { | |
if ((Hob.ResourceDescriptor->ResourceType == Type) && | |
(Hob.ResourceDescriptor->PhysicalStart < End)) | |
{ | |
if (!ResourceDescriptor || | |
(ResourceDescriptor->PhysicalStart < Hob.ResourceDescriptor->PhysicalStart)) | |
{ | |
ResourceDescriptor = Hob.ResourceDescriptor; | |
} | |
} | |
Hob.Raw = GET_NEXT_HOB (Hob); | |
Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw); | |
} | |
return ResourceDescriptor; | |
} | |
/** | |
Set the shared bit for mmio region in Tdx guest. | |
In Tdx guest there are 2 ways to access mmio, TdVmcall or direct access. | |
For direct access, the shared bit of the PageTableEntry should be set. | |
The mmio region information is retrieved from hob list. | |
@retval EFI_SUCCESS The shared bit is set successfully. | |
@retval EFI_UNSUPPORTED Setting the shared bit of memory region | |
is not supported | |
**/ | |
EFI_STATUS | |
SetMmioSharedBit ( | |
VOID | |
) | |
{ | |
EFI_PEI_HOB_POINTERS Hob; | |
Hob.Raw = (UINT8 *)GetHobList (); | |
// | |
// Parse the HOB list until end of list or matching type is found. | |
// | |
while (!END_OF_HOB_LIST (Hob)) { | |
if ( (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) | |
&& (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_MEMORY_MAPPED_IO)) | |
{ | |
MemEncryptTdxSetPageSharedBit ( | |
0, | |
Hob.ResourceDescriptor->PhysicalStart, | |
EFI_SIZE_TO_PAGES (Hob.ResourceDescriptor->ResourceLength) | |
); | |
} | |
Hob.Raw = GET_NEXT_HOB (Hob); | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
TdxDxeEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
RETURN_STATUS PcdStatus; | |
EFI_HOB_RESOURCE_DESCRIPTOR *Res = NULL; | |
EFI_HOB_RESOURCE_DESCRIPTOR *MemRes = NULL; | |
EFI_HOB_PLATFORM_INFO *PlatformInfo = NULL; | |
EFI_HOB_GUID_TYPE *GuidHob; | |
UINT32 CpuMaxLogicalProcessorNumber; | |
TD_RETURN_DATA TdReturnData; | |
EFI_EVENT QemuAcpiTableEvent; | |
void *Registration; | |
GuidHob = GetFirstGuidHob (&gUefiOvmfPkgPlatformInfoGuid); | |
if (GuidHob == NULL) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Both Td and Non-Td guest have PlatformInfoHob which contains the HostBridgePciDevId | |
// | |
PlatformInfo = (EFI_HOB_PLATFORM_INFO *)GET_GUID_HOB_DATA (GuidHob); | |
ASSERT (PlatformInfo->HostBridgeDevId != 0); | |
PcdStatus = PcdSet16S (PcdOvmfHostBridgePciDevId, PlatformInfo->HostBridgeDevId); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
#ifdef TDX_PEI_LESS_BOOT | |
// | |
// For Pei-less boot, PlatformInfo contains more information and | |
// need to set PCDs based on these information. | |
// | |
SetPcdSettings (PlatformInfo); | |
#endif | |
if (!TdIsEnabled ()) { | |
// | |
// If it is Non-Td guest, we install gEfiMpInitLibMpDepProtocolGuid so that | |
// MpInitLib will be used in CpuDxe driver. | |
// | |
gBS->InstallProtocolInterface ( | |
&ImageHandle, | |
&gEfiMpInitLibMpDepProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
NULL | |
); | |
return EFI_SUCCESS; | |
} | |
SetMmioSharedBit (); | |
// | |
// It is Td guest, we install gEfiMpInitLibUpDepProtocolGuid so that | |
// MpInitLibUp will be used in CpuDxe driver. | |
// | |
gBS->InstallProtocolInterface ( | |
&ImageHandle, | |
&gEfiMpInitLibUpDepProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
NULL | |
); | |
// | |
// Install MemoryAccept protocol for TDX | |
// | |
Status = gBS->InstallProtocolInterface ( | |
&mTdxDxeHandle, | |
&gEdkiiMemoryAcceptProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
&mMemoryAcceptProtocol | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Install EdkiiMemoryAcceptProtocol failed.\n")); | |
} | |
// | |
// Call TDINFO to get actual number of cpus in domain | |
// | |
Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData); | |
ASSERT (Status == EFI_SUCCESS); | |
CpuMaxLogicalProcessorNumber = PcdGet32 (PcdCpuMaxLogicalProcessorNumber); | |
// | |
// Adjust PcdCpuMaxLogicalProcessorNumber, if needed. If firmware is configured for | |
// more than number of reported cpus, update. | |
// | |
if (CpuMaxLogicalProcessorNumber > TdReturnData.TdInfo.NumVcpus) { | |
PcdStatus = PcdSet32S (PcdCpuMaxLogicalProcessorNumber, TdReturnData.TdInfo.NumVcpus); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
} | |
// | |
// Register for protocol notifications to call the AlterAcpiTable(), | |
// the protocol will be installed in AcpiPlatformDxe when the ACPI | |
// table provided by Qemu is ready. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
AlterAcpiTable, | |
NULL, | |
&QemuAcpiTableEvent | |
); | |
Status = gBS->RegisterProtocolNotify ( | |
&gQemuAcpiTableNotifyProtocolGuid, | |
QemuAcpiTableEvent, | |
&Registration | |
); | |
#define INIT_PCDSET(NAME, RES) do {\ | |
PcdStatus = PcdSet64S (NAME##Base, (RES)->PhysicalStart); \ | |
ASSERT_RETURN_ERROR (PcdStatus); \ | |
PcdStatus = PcdSet64S (NAME##Size, (RES)->ResourceLength); \ | |
ASSERT_RETURN_ERROR (PcdStatus); \ | |
} while(0) | |
if (PlatformInfo) { | |
PcdSet16S (PcdOvmfHostBridgePciDevId, PlatformInfo->HostBridgeDevId); | |
if ((Res = GetResourceDescriptor (EFI_RESOURCE_MEMORY_MAPPED_IO, (EFI_PHYSICAL_ADDRESS)0x100000000, (EFI_PHYSICAL_ADDRESS)-1)) != NULL) { | |
INIT_PCDSET (PcdPciMmio64, Res); | |
} | |
if ((Res = GetResourceDescriptor (EFI_RESOURCE_IO, 0, 0x10001)) != NULL) { | |
INIT_PCDSET (PcdPciIo, Res); | |
} | |
// | |
// To find low mmio, first find top of low memory, and then search for io space. | |
// | |
if ((MemRes = GetHighestResourceDescriptor (EFI_RESOURCE_SYSTEM_MEMORY, 0xffc00000)) != NULL) { | |
if ((Res = GetResourceDescriptor (EFI_RESOURCE_MEMORY_MAPPED_IO, MemRes->PhysicalStart, 0x100000000)) != NULL) { | |
INIT_PCDSET (PcdPciMmio32, Res); | |
} | |
} | |
} | |
return EFI_SUCCESS; | |
} |