audk/OvmfPkg/TdxDxe/TdxDxe.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

451 lines
13 KiB
C
Raw Normal View History

OvmfPkg: Add TdxDxe driver RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429 TdxDxe driver is dispatched early in DXE, due to being list in APRIORI. This module is responsible for below features: - Sets max logical cpus based on TDINFO - Sets PCI PCDs based on resource hobs - Set shared bit in MMIO region - Relocate Td mailbox and set its address in MADT table. 1. Set shared bit in MMIO region Qemu allows a ROM device to set to ROMD mode (default) or MMIO mode. When it is in ROMD mode, the device is mapped to guest memory and satisfies read access directly. In EDK2 Option ROM is treated as MMIO region. So Tdx guest access Option ROM via TDVMCALL(MMIO). But as explained above, since Qemu set the Option ROM to ROMD mode, the call of TDVMCALL(MMIO) always return INVALID_OPERAND. Tdvf then falls back to direct access. This requires to set the shared bit to corresponding PageTable entry. Otherwise it triggers GP fault. TdxDxe's entry point is the right place to set the shared bit in MMIO region because Option ROM has not been discoverd yet. 2. Relocate Td mailbox and set the new address in MADT Mutiprocessor Wakeup Table. In TDX the guest firmware is designed to publish a multiprocessor-wakeup structure to let the guest-bootstrap processor wake up guest-application processors with a mailbox. The mailbox is memory that the guest firmware can reserve so each guest virtual processor can have the guest OS send a message to them. The address of the mailbox is recorded in the MADT table. See [ACPI]. TdxDxe registers for protocol notification (gQemuAcpiTableNotifyProtocolGuid) to call the AlterAcpiTable(), in which MADT table is altered by the above Mailbox address. The protocol will be installed in AcpiPlatformDxe when the MADT table provided by Qemu is ready. This is to maintain the simplicity of the AcpiPlatformDxe. AlterAcpiTable is the registered function which traverses the ACPI table list to find the original MADT from Qemu. After the new MADT is configured and installed, the original one will be uninstalled. [ACPI] https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model /ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Jordan Justen <jordan.l.justen@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: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Min Xu <min.m.xu@intel.com>
2021-09-22 07:26:01 +02:00
/** @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>
OvmfPkg: Add TdxDxe driver RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429 TdxDxe driver is dispatched early in DXE, due to being list in APRIORI. This module is responsible for below features: - Sets max logical cpus based on TDINFO - Sets PCI PCDs based on resource hobs - Set shared bit in MMIO region - Relocate Td mailbox and set its address in MADT table. 1. Set shared bit in MMIO region Qemu allows a ROM device to set to ROMD mode (default) or MMIO mode. When it is in ROMD mode, the device is mapped to guest memory and satisfies read access directly. In EDK2 Option ROM is treated as MMIO region. So Tdx guest access Option ROM via TDVMCALL(MMIO). But as explained above, since Qemu set the Option ROM to ROMD mode, the call of TDVMCALL(MMIO) always return INVALID_OPERAND. Tdvf then falls back to direct access. This requires to set the shared bit to corresponding PageTable entry. Otherwise it triggers GP fault. TdxDxe's entry point is the right place to set the shared bit in MMIO region because Option ROM has not been discoverd yet. 2. Relocate Td mailbox and set the new address in MADT Mutiprocessor Wakeup Table. In TDX the guest firmware is designed to publish a multiprocessor-wakeup structure to let the guest-bootstrap processor wake up guest-application processors with a mailbox. The mailbox is memory that the guest firmware can reserve so each guest virtual processor can have the guest OS send a message to them. The address of the mailbox is recorded in the MADT table. See [ACPI]. TdxDxe registers for protocol notification (gQemuAcpiTableNotifyProtocolGuid) to call the AlterAcpiTable(), in which MADT table is altered by the above Mailbox address. The protocol will be installed in AcpiPlatformDxe when the MADT table provided by Qemu is ready. This is to maintain the simplicity of the AcpiPlatformDxe. AlterAcpiTable is the registered function which traverses the ACPI table list to find the original MADT from Qemu. After the new MADT is configured and installed, the original one will be uninstalled. [ACPI] https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model /ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Jordan Justen <jordan.l.justen@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: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Min Xu <min.m.xu@intel.com>
2021-09-22 07:26:01 +02:00
#include <Library/UefiBootServicesTableLib.h>
#include <ConfidentialComputingGuestAttr.h>
OvmfPkg: Add TdxDxe driver RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429 TdxDxe driver is dispatched early in DXE, due to being list in APRIORI. This module is responsible for below features: - Sets max logical cpus based on TDINFO - Sets PCI PCDs based on resource hobs - Set shared bit in MMIO region - Relocate Td mailbox and set its address in MADT table. 1. Set shared bit in MMIO region Qemu allows a ROM device to set to ROMD mode (default) or MMIO mode. When it is in ROMD mode, the device is mapped to guest memory and satisfies read access directly. In EDK2 Option ROM is treated as MMIO region. So Tdx guest access Option ROM via TDVMCALL(MMIO). But as explained above, since Qemu set the Option ROM to ROMD mode, the call of TDVMCALL(MMIO) always return INVALID_OPERAND. Tdvf then falls back to direct access. This requires to set the shared bit to corresponding PageTable entry. Otherwise it triggers GP fault. TdxDxe's entry point is the right place to set the shared bit in MMIO region because Option ROM has not been discoverd yet. 2. Relocate Td mailbox and set the new address in MADT Mutiprocessor Wakeup Table. In TDX the guest firmware is designed to publish a multiprocessor-wakeup structure to let the guest-bootstrap processor wake up guest-application processors with a mailbox. The mailbox is memory that the guest firmware can reserve so each guest virtual processor can have the guest OS send a message to them. The address of the mailbox is recorded in the MADT table. See [ACPI]. TdxDxe registers for protocol notification (gQemuAcpiTableNotifyProtocolGuid) to call the AlterAcpiTable(), in which MADT table is altered by the above Mailbox address. The protocol will be installed in AcpiPlatformDxe when the MADT table provided by Qemu is ready. This is to maintain the simplicity of the AcpiPlatformDxe. AlterAcpiTable is the registered function which traverses the ACPI table list to find the original MADT from Qemu. After the new MADT is configured and installed, the original one will be uninstalled. [ACPI] https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model /ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Jordan Justen <jordan.l.justen@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: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Min Xu <min.m.xu@intel.com>
2021-09-22 07:26:01 +02:00
#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);
}
OvmfPkg: Add TdxDxe driver RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429 TdxDxe driver is dispatched early in DXE, due to being list in APRIORI. This module is responsible for below features: - Sets max logical cpus based on TDINFO - Sets PCI PCDs based on resource hobs - Set shared bit in MMIO region - Relocate Td mailbox and set its address in MADT table. 1. Set shared bit in MMIO region Qemu allows a ROM device to set to ROMD mode (default) or MMIO mode. When it is in ROMD mode, the device is mapped to guest memory and satisfies read access directly. In EDK2 Option ROM is treated as MMIO region. So Tdx guest access Option ROM via TDVMCALL(MMIO). But as explained above, since Qemu set the Option ROM to ROMD mode, the call of TDVMCALL(MMIO) always return INVALID_OPERAND. Tdvf then falls back to direct access. This requires to set the shared bit to corresponding PageTable entry. Otherwise it triggers GP fault. TdxDxe's entry point is the right place to set the shared bit in MMIO region because Option ROM has not been discoverd yet. 2. Relocate Td mailbox and set the new address in MADT Mutiprocessor Wakeup Table. In TDX the guest firmware is designed to publish a multiprocessor-wakeup structure to let the guest-bootstrap processor wake up guest-application processors with a mailbox. The mailbox is memory that the guest firmware can reserve so each guest virtual processor can have the guest OS send a message to them. The address of the mailbox is recorded in the MADT table. See [ACPI]. TdxDxe registers for protocol notification (gQemuAcpiTableNotifyProtocolGuid) to call the AlterAcpiTable(), in which MADT table is altered by the above Mailbox address. The protocol will be installed in AcpiPlatformDxe when the MADT table provided by Qemu is ready. This is to maintain the simplicity of the AcpiPlatformDxe. AlterAcpiTable is the registered function which traverses the ACPI table list to find the original MADT from Qemu. After the new MADT is configured and installed, the original one will be uninstalled. [ACPI] https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model /ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Jordan Justen <jordan.l.justen@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: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Min Xu <min.m.xu@intel.com>
2021-09-22 07:26:01 +02:00
/**
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
OvmfPkg: Add TdxDxe driver RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429 TdxDxe driver is dispatched early in DXE, due to being list in APRIORI. This module is responsible for below features: - Sets max logical cpus based on TDINFO - Sets PCI PCDs based on resource hobs - Set shared bit in MMIO region - Relocate Td mailbox and set its address in MADT table. 1. Set shared bit in MMIO region Qemu allows a ROM device to set to ROMD mode (default) or MMIO mode. When it is in ROMD mode, the device is mapped to guest memory and satisfies read access directly. In EDK2 Option ROM is treated as MMIO region. So Tdx guest access Option ROM via TDVMCALL(MMIO). But as explained above, since Qemu set the Option ROM to ROMD mode, the call of TDVMCALL(MMIO) always return INVALID_OPERAND. Tdvf then falls back to direct access. This requires to set the shared bit to corresponding PageTable entry. Otherwise it triggers GP fault. TdxDxe's entry point is the right place to set the shared bit in MMIO region because Option ROM has not been discoverd yet. 2. Relocate Td mailbox and set the new address in MADT Mutiprocessor Wakeup Table. In TDX the guest firmware is designed to publish a multiprocessor-wakeup structure to let the guest-bootstrap processor wake up guest-application processors with a mailbox. The mailbox is memory that the guest firmware can reserve so each guest virtual processor can have the guest OS send a message to them. The address of the mailbox is recorded in the MADT table. See [ACPI]. TdxDxe registers for protocol notification (gQemuAcpiTableNotifyProtocolGuid) to call the AlterAcpiTable(), in which MADT table is altered by the above Mailbox address. The protocol will be installed in AcpiPlatformDxe when the MADT table provided by Qemu is ready. This is to maintain the simplicity of the AcpiPlatformDxe. AlterAcpiTable is the registered function which traverses the ACPI table list to find the original MADT from Qemu. After the new MADT is configured and installed, the original one will be uninstalled. [ACPI] https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model /ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Jordan Justen <jordan.l.justen@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: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Min Xu <min.m.xu@intel.com>
2021-09-22 07:26:01 +02:00
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;
OvmfPkg: Add TdxDxe driver RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429 TdxDxe driver is dispatched early in DXE, due to being list in APRIORI. This module is responsible for below features: - Sets max logical cpus based on TDINFO - Sets PCI PCDs based on resource hobs - Set shared bit in MMIO region - Relocate Td mailbox and set its address in MADT table. 1. Set shared bit in MMIO region Qemu allows a ROM device to set to ROMD mode (default) or MMIO mode. When it is in ROMD mode, the device is mapped to guest memory and satisfies read access directly. In EDK2 Option ROM is treated as MMIO region. So Tdx guest access Option ROM via TDVMCALL(MMIO). But as explained above, since Qemu set the Option ROM to ROMD mode, the call of TDVMCALL(MMIO) always return INVALID_OPERAND. Tdvf then falls back to direct access. This requires to set the shared bit to corresponding PageTable entry. Otherwise it triggers GP fault. TdxDxe's entry point is the right place to set the shared bit in MMIO region because Option ROM has not been discoverd yet. 2. Relocate Td mailbox and set the new address in MADT Mutiprocessor Wakeup Table. In TDX the guest firmware is designed to publish a multiprocessor-wakeup structure to let the guest-bootstrap processor wake up guest-application processors with a mailbox. The mailbox is memory that the guest firmware can reserve so each guest virtual processor can have the guest OS send a message to them. The address of the mailbox is recorded in the MADT table. See [ACPI]. TdxDxe registers for protocol notification (gQemuAcpiTableNotifyProtocolGuid) to call the AlterAcpiTable(), in which MADT table is altered by the above Mailbox address. The protocol will be installed in AcpiPlatformDxe when the MADT table provided by Qemu is ready. This is to maintain the simplicity of the AcpiPlatformDxe. AlterAcpiTable is the registered function which traverses the ACPI table list to find the original MADT from Qemu. After the new MADT is configured and installed, the original one will be uninstalled. [ACPI] https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model /ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Jordan Justen <jordan.l.justen@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: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Min Xu <min.m.xu@intel.com>
2021-09-22 07:26:01 +02:00
}
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"));
}
OvmfPkg: Add TdxDxe driver RFC: https://bugzilla.tianocore.org/show_bug.cgi?id=3429 TdxDxe driver is dispatched early in DXE, due to being list in APRIORI. This module is responsible for below features: - Sets max logical cpus based on TDINFO - Sets PCI PCDs based on resource hobs - Set shared bit in MMIO region - Relocate Td mailbox and set its address in MADT table. 1. Set shared bit in MMIO region Qemu allows a ROM device to set to ROMD mode (default) or MMIO mode. When it is in ROMD mode, the device is mapped to guest memory and satisfies read access directly. In EDK2 Option ROM is treated as MMIO region. So Tdx guest access Option ROM via TDVMCALL(MMIO). But as explained above, since Qemu set the Option ROM to ROMD mode, the call of TDVMCALL(MMIO) always return INVALID_OPERAND. Tdvf then falls back to direct access. This requires to set the shared bit to corresponding PageTable entry. Otherwise it triggers GP fault. TdxDxe's entry point is the right place to set the shared bit in MMIO region because Option ROM has not been discoverd yet. 2. Relocate Td mailbox and set the new address in MADT Mutiprocessor Wakeup Table. In TDX the guest firmware is designed to publish a multiprocessor-wakeup structure to let the guest-bootstrap processor wake up guest-application processors with a mailbox. The mailbox is memory that the guest firmware can reserve so each guest virtual processor can have the guest OS send a message to them. The address of the mailbox is recorded in the MADT table. See [ACPI]. TdxDxe registers for protocol notification (gQemuAcpiTableNotifyProtocolGuid) to call the AlterAcpiTable(), in which MADT table is altered by the above Mailbox address. The protocol will be installed in AcpiPlatformDxe when the MADT table provided by Qemu is ready. This is to maintain the simplicity of the AcpiPlatformDxe. AlterAcpiTable is the registered function which traverses the ACPI table list to find the original MADT from Qemu. After the new MADT is configured and installed, the original one will be uninstalled. [ACPI] https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model /ACPI_Software_Programming_Model.html#multiprocessor-wakeup-structure Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Jordan Justen <jordan.l.justen@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: Jiewen Yao <jiewen.yao@intel.com> Signed-off-by: Min Xu <min.m.xu@intel.com>
2021-09-22 07:26:01 +02:00
//
// 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;
}