/** @file
Copyright (c) 2020, 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 "IntelVTdDmarPei.h"
EFI_GUID mVTdInfoGuid = {
0x222f5e30, 0x5cd, 0x49c6, { 0x8a, 0xc, 0x36, 0xd6, 0x58, 0x41, 0xe0, 0x82 }
};
EFI_GUID mDmaBufferInfoGuid = {
0x7b624ec7, 0xfb67, 0x4f9c, { 0xb6, 0xb0, 0x4d, 0xfa, 0x9c, 0x88, 0x20, 0x39 }
};
#define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P')
typedef struct {
UINT32 Signature;
EDKII_IOMMU_OPERATION Operation;
UINTN NumberOfBytes;
EFI_PHYSICAL_ADDRESS HostAddress;
EFI_PHYSICAL_ADDRESS DeviceAddress;
} MAP_INFO;
/**
Set IOMMU attribute for a system memory.
If the IOMMU PPI exists, the system memory cannot be used
for DMA by default.
When a device requests a DMA access for a system memory,
the device driver need use SetAttribute() to update the IOMMU
attribute to request DMA access (read and/or write).
@param[in] This The PPI instance pointer.
@param[in] DeviceHandle The device who initiates the DMA access request.
@param[in] Mapping The mapping value returned from Map().
@param[in] IoMmuAccess The IOMMU access.
@retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by DeviceAddress and Length.
@retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
@retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access.
@retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported by the IOMMU.
@retval EFI_UNSUPPORTED The IOMMU does not support the memory range specified by Mapping.
@retval EFI_OUT_OF_RESOURCES There are not enough resources available to modify the IOMMU access.
@retval EFI_DEVICE_ERROR The IOMMU device reported an error while attempting the operation.
@retval EFI_NOT_AVAILABLE_YET DMA protection has been enabled, but DMA buffer are
not available to be allocated yet.
**/
EFI_STATUS
EFIAPI
PeiIoMmuSetAttribute (
IN EDKII_IOMMU_PPI *This,
IN VOID *Mapping,
IN UINT64 IoMmuAccess
)
{
VOID *Hob;
DMA_BUFFER_INFO *DmaBufferInfo;
DEBUG ((DEBUG_INFO, "PeiIoMmuSetAttribute:\n"));
Hob = GetFirstGuidHob (&mDmaBufferInfoGuid);
DmaBufferInfo = GET_GUID_HOB_DATA(Hob);
if (DmaBufferInfo->DmaBufferCurrentTop == 0) {
DEBUG ((DEBUG_INFO, "PeiIoMmuSetAttribute: DmaBufferCurrentTop == 0\n"));
return EFI_NOT_AVAILABLE_YET;
}
return EFI_SUCCESS;
}
/**
Provides the controller-specific addresses required to access system memory from a
DMA bus master.
@param This The PPI instance pointer.
@param Operation Indicates if the bus master is going to read or write to system memory.
@param HostAddress The system memory address to map to the PCI controller.
@param NumberOfBytes On input the number of bytes to map. On output the number of bytes
that were mapped.
@param DeviceAddress The resulting map address for the bus master PCI controller to use to
access the hosts HostAddress.
@param Mapping A resulting value to pass to Unmap().
@retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes.
@retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
@retval EFI_DEVICE_ERROR The system hardware could not map the requested address.
@retval EFI_NOT_AVAILABLE_YET DMA protection has been enabled, but DMA buffer are
not available to be allocated yet.
**/
EFI_STATUS
EFIAPI
PeiIoMmuMap (
IN EDKII_IOMMU_PPI *This,
IN EDKII_IOMMU_OPERATION Operation,
IN VOID *HostAddress,
IN OUT UINTN *NumberOfBytes,
OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
OUT VOID **Mapping
)
{
MAP_INFO *MapInfo;
UINTN Length;
VOID *Hob;
DMA_BUFFER_INFO *DmaBufferInfo;
Hob = GetFirstGuidHob (&mDmaBufferInfoGuid);
DmaBufferInfo = GET_GUID_HOB_DATA(Hob);
DEBUG ((DEBUG_INFO, "PeiIoMmuMap - HostAddress - 0x%x, NumberOfBytes - %x\n", HostAddress, *NumberOfBytes));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentTop - %x\n", DmaBufferInfo->DmaBufferCurrentTop));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentBottom - %x\n", DmaBufferInfo->DmaBufferCurrentBottom));
DEBUG ((DEBUG_INFO, " Operation - %x\n", Operation));
if (DmaBufferInfo->DmaBufferCurrentTop == 0) {
return EFI_NOT_AVAILABLE_YET;
}
if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
*DeviceAddress = (UINTN)HostAddress;
*Mapping = NULL;
return EFI_SUCCESS;
}
Length = *NumberOfBytes + sizeof (MAP_INFO);
if (Length > DmaBufferInfo->DmaBufferCurrentTop - DmaBufferInfo->DmaBufferCurrentBottom) {
DEBUG ((DEBUG_ERROR, "PeiIoMmuMap - OUT_OF_RESOURCE\n"));
ASSERT (FALSE);
return EFI_OUT_OF_RESOURCES;
}
*DeviceAddress = DmaBufferInfo->DmaBufferCurrentBottom;
DmaBufferInfo->DmaBufferCurrentBottom += Length;
MapInfo = (VOID *) (UINTN) (*DeviceAddress + *NumberOfBytes);
MapInfo->Signature = MAP_INFO_SIGNATURE;
MapInfo->Operation = Operation;
MapInfo->NumberOfBytes = *NumberOfBytes;
MapInfo->HostAddress = (UINTN) HostAddress;
MapInfo->DeviceAddress = *DeviceAddress;
*Mapping = MapInfo;
DEBUG ((DEBUG_INFO, " Op(%x):DeviceAddress - %x, Mapping - %x\n", Operation, (UINTN) *DeviceAddress, MapInfo));
//
// If this is a read operation from the Bus Master's point of view,
// then copy the contents of the real buffer into the mapped buffer
// so the Bus Master can read the contents of the real buffer.
//
if (Operation == EdkiiIoMmuOperationBusMasterRead ||
Operation == EdkiiIoMmuOperationBusMasterRead64) {
CopyMem (
(VOID *) (UINTN) MapInfo->DeviceAddress,
(VOID *) (UINTN) MapInfo->HostAddress,
MapInfo->NumberOfBytes
);
}
return EFI_SUCCESS;
}
/**
Completes the Map() operation and releases any corresponding resources.
@param This The PPI instance pointer.
@param Mapping The mapping value returned from Map().
@retval EFI_SUCCESS The range was unmapped.
@retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map().
@retval EFI_DEVICE_ERROR The data was not committed to the target system memory.
@retval EFI_NOT_AVAILABLE_YET DMA protection has been enabled, but DMA buffer are
not available to be allocated yet.
**/
EFI_STATUS
EFIAPI
PeiIoMmuUnmap (
IN EDKII_IOMMU_PPI *This,
IN VOID *Mapping
)
{
MAP_INFO *MapInfo;
UINTN Length;
VOID *Hob;
DMA_BUFFER_INFO *DmaBufferInfo;
Hob = GetFirstGuidHob (&mDmaBufferInfoGuid);
DmaBufferInfo = GET_GUID_HOB_DATA(Hob);
DEBUG ((DEBUG_INFO, "PeiIoMmuUnmap - Mapping - %x\n", Mapping));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentTop - %x\n", DmaBufferInfo->DmaBufferCurrentTop));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentBottom - %x\n", DmaBufferInfo->DmaBufferCurrentBottom));
if (DmaBufferInfo->DmaBufferCurrentTop == 0) {
return EFI_NOT_AVAILABLE_YET;
}
if (Mapping == NULL) {
return EFI_SUCCESS;
}
MapInfo = Mapping;
ASSERT (MapInfo->Signature == MAP_INFO_SIGNATURE);
DEBUG ((DEBUG_INFO, " Op(%x):DeviceAddress - %x, NumberOfBytes - %x\n", MapInfo->Operation, (UINTN) MapInfo->DeviceAddress, MapInfo->NumberOfBytes));
//
// If this is a write operation from the Bus Master's point of view,
// then copy the contents of the mapped buffer into the real buffer
// so the processor can read the contents of the real buffer.
//
if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
CopyMem (
(VOID *) (UINTN) MapInfo->HostAddress,
(VOID *) (UINTN) MapInfo->DeviceAddress,
MapInfo->NumberOfBytes
);
}
Length = MapInfo->NumberOfBytes + sizeof (MAP_INFO);
if (DmaBufferInfo->DmaBufferCurrentBottom == MapInfo->DeviceAddress + Length) {
DmaBufferInfo->DmaBufferCurrentBottom -= Length;
}
return EFI_SUCCESS;
}
/**
Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
OperationBusMasterCommonBuffer64 mapping.
@param This The PPI instance pointer.
@param MemoryType The type of memory to allocate, EfiBootServicesData or
EfiRuntimeServicesData.
@param Pages The number of pages to allocate.
@param HostAddress A pointer to store the base system memory address of the
allocated range.
@param Attributes The requested bit mask of attributes for the allocated range.
@retval EFI_SUCCESS The requested memory pages were allocated.
@retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are
MEMORY_WRITE_COMBINE, MEMORY_CACHED and DUAL_ADDRESS_CYCLE.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
@retval EFI_NOT_AVAILABLE_YET DMA protection has been enabled, but DMA buffer are
not available to be allocated yet.
**/
EFI_STATUS
EFIAPI
PeiIoMmuAllocateBuffer (
IN EDKII_IOMMU_PPI *This,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN OUT VOID **HostAddress,
IN UINT64 Attributes
)
{
UINTN Length;
VOID *Hob;
DMA_BUFFER_INFO *DmaBufferInfo;
Hob = GetFirstGuidHob (&mDmaBufferInfoGuid);
DmaBufferInfo = GET_GUID_HOB_DATA(Hob);
DEBUG ((DEBUG_INFO, "PeiIoMmuAllocateBuffer - page - %x\n", Pages));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentTop - %x\n", DmaBufferInfo->DmaBufferCurrentTop));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentBottom - %x\n", DmaBufferInfo->DmaBufferCurrentBottom));
if (DmaBufferInfo->DmaBufferCurrentTop == 0) {
return EFI_NOT_AVAILABLE_YET;
}
Length = EFI_PAGES_TO_SIZE (Pages);
if (Length > DmaBufferInfo->DmaBufferCurrentTop - DmaBufferInfo->DmaBufferCurrentBottom) {
DEBUG ((DEBUG_ERROR, "PeiIoMmuAllocateBuffer - OUT_OF_RESOURCE\n"));
ASSERT (FALSE);
return EFI_OUT_OF_RESOURCES;
}
*HostAddress = (VOID *) (UINTN) (DmaBufferInfo->DmaBufferCurrentTop - Length);
DmaBufferInfo->DmaBufferCurrentTop -= Length;
DEBUG ((DEBUG_INFO, "PeiIoMmuAllocateBuffer - allocate - %x\n", *HostAddress));
return EFI_SUCCESS;
}
/**
Frees memory that was allocated with AllocateBuffer().
@param This The PPI instance pointer.
@param Pages The number of pages to free.
@param HostAddress The base system memory address of the allocated range.
@retval EFI_SUCCESS The requested memory pages were freed.
@retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages
was not allocated with AllocateBuffer().
@retval EFI_NOT_AVAILABLE_YET DMA protection has been enabled, but DMA buffer are
not available to be allocated yet.
**/
EFI_STATUS
EFIAPI
PeiIoMmuFreeBuffer (
IN EDKII_IOMMU_PPI *This,
IN UINTN Pages,
IN VOID *HostAddress
)
{
UINTN Length;
VOID *Hob;
DMA_BUFFER_INFO *DmaBufferInfo;
Hob = GetFirstGuidHob (&mDmaBufferInfoGuid);
DmaBufferInfo = GET_GUID_HOB_DATA (Hob);
DEBUG ((DEBUG_INFO, "PeiIoMmuFreeBuffer - page - %x, HostAddr - %x\n", Pages, HostAddress));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentTop - %x\n", DmaBufferInfo->DmaBufferCurrentTop));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentBottom - %x\n", DmaBufferInfo->DmaBufferCurrentBottom));
if (DmaBufferInfo->DmaBufferCurrentTop == 0) {
return EFI_NOT_AVAILABLE_YET;
}
Length = EFI_PAGES_TO_SIZE (Pages);
if ((UINTN)HostAddress == DmaBufferInfo->DmaBufferCurrentTop) {
DmaBufferInfo->DmaBufferCurrentTop += Length;
}
return EFI_SUCCESS;
}
EDKII_IOMMU_PPI mIoMmuPpi = {
EDKII_IOMMU_PPI_REVISION,
PeiIoMmuSetAttribute,
PeiIoMmuMap,
PeiIoMmuUnmap,
PeiIoMmuAllocateBuffer,
PeiIoMmuFreeBuffer,
};
CONST EFI_PEI_PPI_DESCRIPTOR mIoMmuPpiList = {
EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
&gEdkiiIoMmuPpiGuid,
(VOID *) &mIoMmuPpi
};
/**
Release the momery in the Intel VTd Info
@param[in] VTdInfo The VTd engine context information.
**/
VOID
ReleaseVTdInfo (
IN VTD_INFO *VTdInfo
)
{
UINTN Index;
for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
DEBUG ((DEBUG_INFO, "Release momery in VTdInfo[%d]\n", Index));
if (VTdInfo->VtdUnitInfo[Index].FixedSecondLevelPagingEntry) {
FreePages ((VOID *) (UINTN) VTdInfo->VtdUnitInfo[Index].FixedSecondLevelPagingEntry, 1);
VTdInfo->VtdUnitInfo[Index].FixedSecondLevelPagingEntry = 0;
}
if (VTdInfo->VtdUnitInfo[Index].RmrrSecondLevelPagingEntry) {
FreePages ((VOID *) (UINTN) VTdInfo->VtdUnitInfo[Index].RmrrSecondLevelPagingEntry, 1);
VTdInfo->VtdUnitInfo[Index].RmrrSecondLevelPagingEntry = 0;
}
if (VTdInfo->VtdUnitInfo[Index].RootEntryTable) {
FreePages ((VOID *) (UINTN) VTdInfo->VtdUnitInfo[Index].RootEntryTable, VTdInfo->VtdUnitInfo[Index].RootEntryTablePageSize);
VTdInfo->VtdUnitInfo[Index].RootEntryTable = 0;
}
if (VTdInfo->VtdUnitInfo[Index].ExtRootEntryTable) {
FreePages ((VOID *) (UINTN) VTdInfo->VtdUnitInfo[Index].ExtRootEntryTable, VTdInfo->VtdUnitInfo[Index].ExtRootEntryTablePageSize);
VTdInfo->VtdUnitInfo[Index].RootEntryTable = 0;
}
if (VTdInfo->VtdUnitInfo[Index].PciDeviceInfo.PciDeviceData) {
FreePages ((VOID *) (UINTN) VTdInfo->VtdUnitInfo[Index].PciDeviceInfo.PciDeviceData, VTdInfo->VtdUnitInfo[Index].PciDeviceInfo.PciDeviceDataPageSize);
VTdInfo->VtdUnitInfo[Index].PciDeviceInfo.PciDeviceDataPageSize = 0;
VTdInfo->VtdUnitInfo[Index].PciDeviceInfo.PciDeviceData = 0;
VTdInfo->VtdUnitInfo[Index].PciDeviceInfo.PciDeviceDataNumber = 0;
VTdInfo->VtdUnitInfo[Index].PciDeviceInfo.PciDeviceDataMaxNumber = 0;
}
}
}
/**
Initializes the Intel VTd Info.
@retval EFI_SUCCESS Usb bot driver is successfully initialized.
@retval EFI_OUT_OF_RESOURCES Can't initialize the driver.
**/
EFI_STATUS
InitVTdInfo (
VOID
)
{
EFI_STATUS Status;
EFI_ACPI_DMAR_HEADER *AcpiDmarTable;
VOID *Hob;
VTD_INFO *VTdInfo;
UINT64 EngineMask;
Status = PeiServicesLocatePpi (
&gEdkiiVTdInfoPpiGuid,
0,
NULL,
(VOID **)&AcpiDmarTable
);
ASSERT_EFI_ERROR (Status);
DumpAcpiDMAR (AcpiDmarTable);
//
// Clear old VTdInfo Hob.
//
Hob = GetFirstGuidHob (&mVTdInfoGuid);
if (Hob != NULL) {
DEBUG ((DEBUG_INFO, " Find Hob : mVTdInfoGuid - 0x%x\n", Hob));
VTdInfo = GET_GUID_HOB_DATA(Hob);
EngineMask = LShiftU64 (1, VTdInfo->VTdEngineCount) - 1;
EnableVTdTranslationProtectionAll (VTdInfo, EngineMask);
ReleaseVTdInfo (VTdInfo);
VTdInfo->VTdEngineCount = 0;
ZeroMem (&((EFI_HOB_GUID_TYPE *) Hob)->Name, sizeof (EFI_GUID));
}
//
// Get DMAR information to local VTdInfo
//
Status = ParseDmarAcpiTableDrhd (AcpiDmarTable);
if (EFI_ERROR(Status)) {
DEBUG ((DEBUG_ERROR, " ParseDmarAcpiTableDrhd : %r\n", Status));
return Status;
}
//
// NOTE: Do not parse RMRR here, because RMRR may cause DMAR programming.
//
return EFI_SUCCESS;
}
/**
Initializes the Intel VTd DMAR for all memory.
@retval EFI_SUCCESS Driver is successfully initialized.
@retval RETURN_NOT_READY Fail to get VTdInfo Hob .
**/
EFI_STATUS
InitVTdDmarForAll (
VOID
)
{
VOID *Hob;
VTD_INFO *VTdInfo;
UINT64 EngineMask;
EFI_STATUS Status;
Hob = GetFirstGuidHob (&mVTdInfoGuid);
if (Hob == NULL) {
DEBUG ((DEBUG_ERROR, "Fail to get VTdInfo Hob.\n"));
return RETURN_NOT_READY;
}
VTdInfo = GET_GUID_HOB_DATA (Hob);
EngineMask = LShiftU64 (1, VTdInfo->VTdEngineCount) - 1;
DEBUG ((DEBUG_INFO, "PrepareVtdConfig\n"));
Status = PrepareVtdConfig (VTdInfo);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
EnableVTdTranslationProtectionAll (VTdInfo, EngineMask);
return EFI_SUCCESS;
}
/**
Initializes DMA buffer
@retval EFI_SUCCESS DMA buffer is successfully initialized.
@retval EFI_INVALID_PARAMETER Invalid DMA buffer size.
@retval EFI_OUT_OF_RESOURCES Can't initialize DMA buffer.
**/
EFI_STATUS
InitDmaBuffer(
VOID
)
{
DMA_BUFFER_INFO *DmaBufferInfo;
VOID *Hob;
VOID *VtdPmrHobPtr;
VTD_PMR_INFO_HOB *VtdPmrHob;
DEBUG ((DEBUG_INFO, "InitDmaBuffer :\n"));
Hob = GetFirstGuidHob (&mDmaBufferInfoGuid);
DmaBufferInfo = GET_GUID_HOB_DATA (Hob);
VtdPmrHobPtr = GetFirstGuidHob (&gVtdPmrInfoDataHobGuid);
/**
When gVtdPmrInfoDataHobGuid exists, it means:
1. Dma buffer is reserved by memory initialize code
2. PeiGetVtdPmrAlignmentLib is used to get alignment
3. Protection regions are determined by the system memory map
4. Protection regions will be conveyed through VTD_PMR_INFO_HOB
When gVtdPmrInfoDataHobGuid dosen't exist, it means:
1. IntelVTdDmar driver will calcuate the PMR memory alignment
2. Dma buffer is reserved by AllocateAlignedPages()
**/
if (DmaBufferInfo->DmaBufferSize == 0) {
DEBUG ((DEBUG_INFO, " DmaBufferSize is 0\n"));
return EFI_INVALID_PARAMETER;
}
if (VtdPmrHobPtr == NULL) {
//
// Allocate memory for DMA buffer
//
DmaBufferInfo->DmaBufferBase = (UINT64) (UINTN) AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINTN) DmaBufferInfo->DmaBufferSize), 0);
if (DmaBufferInfo->DmaBufferBase == 0) {
DEBUG ((DEBUG_ERROR, " InitDmaBuffer : OutOfResource\n"));
return EFI_OUT_OF_RESOURCES;
}
DmaBufferInfo->DmaBufferLimit = DmaBufferInfo->DmaBufferBase + DmaBufferInfo->DmaBufferSize;
DEBUG ((DEBUG_INFO, "Alloc DMA buffer success.\n"));
} else {
//
// Get the PMR ranges information for the VTd PMR hob
//
VtdPmrHob = GET_GUID_HOB_DATA (VtdPmrHobPtr);
DmaBufferInfo->DmaBufferBase = VtdPmrHob->ProtectedLowLimit;
DmaBufferInfo->DmaBufferLimit = VtdPmrHob->ProtectedHighBase;
}
DmaBufferInfo->DmaBufferCurrentTop = DmaBufferInfo->DmaBufferBase + DmaBufferInfo->DmaBufferSize;
DmaBufferInfo->DmaBufferCurrentBottom = DmaBufferInfo->DmaBufferBase;
DEBUG ((DEBUG_INFO, " DmaBufferSize : 0x%lx\n", DmaBufferInfo->DmaBufferSize));
DEBUG ((DEBUG_INFO, " DmaBufferBase : 0x%lx\n", DmaBufferInfo->DmaBufferBase));
DEBUG ((DEBUG_INFO, " DmaBufferLimit : 0x%lx\n", DmaBufferInfo->DmaBufferLimit));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentTop : 0x%lx\n", DmaBufferInfo->DmaBufferCurrentTop));
DEBUG ((DEBUG_INFO, " DmaBufferCurrentBottom : 0x%lx\n", DmaBufferInfo->DmaBufferCurrentBottom));
return EFI_SUCCESS;
}
/**
Initializes the Intel VTd DMAR for DMA buffer.
@retval EFI_SUCCESS Usb bot driver is successfully initialized.
@retval EFI_OUT_OF_RESOURCES Can't initialize the driver.
@retval EFI_DEVICE_ERROR DMAR translation is not enabled.
**/
EFI_STATUS
InitVTdDmarForDma (
VOID
)
{
VOID *Hob;
VTD_INFO *VTdInfo;
EFI_STATUS Status;
EFI_PEI_PPI_DESCRIPTOR *OldDescriptor;
EDKII_IOMMU_PPI *OldIoMmuPpi;
Hob = GetFirstGuidHob (&mVTdInfoGuid);
VTdInfo = GET_GUID_HOB_DATA (Hob);
DEBUG ((DEBUG_INFO, "PrepareVtdConfig\n"));
Status = PrepareVtdConfig (VTdInfo);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
DEBUG ((DEBUG_INFO, "PrepareVtdCacheInvalidationConfig\n"));
Status = PrepareVtdCacheInvalidationConfig (VTdInfo);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
// create root entry table
DEBUG ((DEBUG_INFO, "SetupTranslationTable\n"));
Status = SetupTranslationTable (VTdInfo);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
// If there is RMRR memory, parse it here.
DEBUG ((DEBUG_INFO, "PeiParseDmarAcpiTableRmrr\n"));
ParseDmarAcpiTableRmrr (VTdInfo);
DEBUG ((DEBUG_INFO, "EnableVtdDmar\n"));
Status = EnableVTdTranslationProtection(VTdInfo);
if (EFI_ERROR (Status)) {
return Status;
}
DEBUG ((DEBUG_INFO, "Install gEdkiiIoMmuPpiGuid\n"));
// install protocol
//
// (Re)Install PPI.
//
Status = PeiServicesLocatePpi (
&gEdkiiIoMmuPpiGuid,
0,
&OldDescriptor,
(VOID **) &OldIoMmuPpi
);
if (!EFI_ERROR (Status)) {
Status = PeiServicesReInstallPpi (OldDescriptor, &mIoMmuPpiList);
} else {
Status = PeiServicesInstallPpi (&mIoMmuPpiList);
}
ASSERT_EFI_ERROR (Status);
return Status;
}
/**
This function handles S3 resume task at the end of PEI
@param[in] PeiServices Pointer to PEI Services Table.
@param[in] NotifyDesc Pointer to the descriptor for the Notification event that
caused this function to execute.
@param[in] Ppi Pointer to the PPI data associated with this function.
@retval EFI_STATUS Always return EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
S3EndOfPeiNotify(
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
IN VOID *Ppi
)
{
VOID *Hob;
VTD_INFO *VTdInfo;
UINT64 EngineMask;
DEBUG((DEBUG_INFO, "VTd DMAR PEI S3EndOfPeiNotify\n"));
if ((PcdGet8(PcdVTdPolicyPropertyMask) & BIT1) == 0) {
Hob = GetFirstGuidHob (&mVTdInfoGuid);
if (Hob == NULL) {
return EFI_SUCCESS;
}
VTdInfo = GET_GUID_HOB_DATA(Hob);
EngineMask = LShiftU64 (1, VTdInfo->VTdEngineCount) - 1;
DisableVTdTranslationProtection (VTdInfo, EngineMask);
}
return EFI_SUCCESS;
}
EFI_PEI_NOTIFY_DESCRIPTOR mS3EndOfPeiNotifyDesc = {
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEfiEndOfPeiSignalPpiGuid,
S3EndOfPeiNotify
};
/**
This function handles VTd engine setup
@param[in] PeiServices Pointer to PEI Services Table.
@param[in] NotifyDesc Pointer to the descriptor for the Notification event that
caused this function to execute.
@param[in] Ppi Pointer to the PPI data associated with this function.
@retval EFI_STATUS Always return EFI_SUCCESS
**/
EFI_STATUS
EFIAPI
VTdInfoNotify (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
IN VOID *Ppi
)
{
EFI_STATUS Status;
VOID *MemoryDiscovered;
BOOLEAN MemoryInitialized;
DEBUG ((DEBUG_INFO, "VTdInfoNotify\n"));
//
// Check if memory is initialized.
//
MemoryInitialized = FALSE;
Status = PeiServicesLocatePpi (
&gEfiPeiMemoryDiscoveredPpiGuid,
0,
NULL,
&MemoryDiscovered
);
if (!EFI_ERROR(Status)) {
MemoryInitialized = TRUE;
}
DEBUG ((DEBUG_INFO, "MemoryInitialized - %x\n", MemoryInitialized));
//
// NOTE: We need reinit VTdInfo because previous information might be overriden.
//
InitVTdInfo ();
if (!MemoryInitialized) {
//
// If the memory is not initialized,
// Protect all system memory
//
InitVTdDmarForAll ();
//
// Install PPI.
//
Status = PeiServicesInstallPpi (&mIoMmuPpiList);
ASSERT_EFI_ERROR(Status);
} else {
//
// If the memory is initialized,
// Allocate DMA buffer and protect rest system memory
//
Status = InitDmaBuffer ();
ASSERT_EFI_ERROR(Status);
InitVTdDmarForDma ();
}
return EFI_SUCCESS;
}
EFI_PEI_NOTIFY_DESCRIPTOR mVTdInfoNotifyDesc = {
(EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
&gEdkiiVTdInfoPpiGuid,
VTdInfoNotify
};
/**
Initializes the Intel VTd DMAR PEIM.
@param[in] FileHandle Handle of the file being invoked.
@param[in] PeiServices Describes the list of possible PEI Services.
@retval EFI_SUCCESS Usb bot driver is successfully initialized.
@retval EFI_OUT_OF_RESOURCES Can't initialize the driver.
**/
EFI_STATUS
EFIAPI
IntelVTdDmarInitialize (
IN EFI_PEI_FILE_HANDLE FileHandle,
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
EFI_BOOT_MODE BootMode;
DMA_BUFFER_INFO *DmaBufferInfo;
DEBUG ((DEBUG_INFO, "IntelVTdDmarInitialize\n"));
if ((PcdGet8(PcdVTdPolicyPropertyMask) & BIT0) == 0) {
return EFI_UNSUPPORTED;
}
DmaBufferInfo = BuildGuidHob (&mDmaBufferInfoGuid, sizeof (DMA_BUFFER_INFO));
ASSERT(DmaBufferInfo != NULL);
if (DmaBufferInfo == NULL) {
return EFI_OUT_OF_RESOURCES;
}
ZeroMem (DmaBufferInfo, sizeof (DMA_BUFFER_INFO));
PeiServicesGetBootMode (&BootMode);
if (BootMode == BOOT_ON_S3_RESUME) {
DmaBufferInfo->DmaBufferSize = PcdGet32 (PcdVTdPeiDmaBufferSizeS3);
} else {
DmaBufferInfo->DmaBufferSize = PcdGet32 (PcdVTdPeiDmaBufferSize);
}
Status = PeiServicesNotifyPpi (&mVTdInfoNotifyDesc);
ASSERT_EFI_ERROR (Status);
//
// Register EndOfPei Notify for S3
//
if (BootMode == BOOT_ON_S3_RESUME) {
Status = PeiServicesNotifyPpi (&mS3EndOfPeiNotifyDesc);
ASSERT_EFI_ERROR (Status);
}
return EFI_SUCCESS;
}