/** @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.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }