IntelSiliconPkg/IntelVTdDxe: Move to feature dir.

Move IntelVTdDxe to Feature/VTd/IntelVTdDxe.

Suggested-by: Star Zeng <star.zeng@intel.com>
Cc: Star Zeng <star.zeng@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jiewen Yao <jiewen.yao@intel.com>
Reviewed-by: Star Zeng <star.zeng@intel.com>
(cherry picked from commit 9010459c9a)
This commit is contained in:
Jiewen Yao
2017-09-16 09:28:05 +08:00
committed by Star Zeng
parent 8f334a80b3
commit f899049159
13 changed files with 1 additions and 1 deletions

View File

@@ -0,0 +1,441 @@
/** @file
BmDma related function
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <PiDxe.h>
#include <Protocol/IoMmu.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
// TBD: May make it a policy
#define DMA_MEMORY_TOP MAX_UINTN
//#define DMA_MEMORY_TOP 0x0000000001FFFFFFULL
#define MAP_INFO_SIGNATURE SIGNATURE_32 ('D', 'M', 'A', 'P')
typedef struct {
UINT32 Signature;
LIST_ENTRY Link;
EDKII_IOMMU_OPERATION Operation;
UINTN NumberOfBytes;
UINTN NumberOfPages;
EFI_PHYSICAL_ADDRESS HostAddress;
EFI_PHYSICAL_ADDRESS DeviceAddress;
} MAP_INFO;
#define MAP_INFO_FROM_LINK(a) CR (a, MAP_INFO, Link, MAP_INFO_SIGNATURE)
LIST_ENTRY gMaps = INITIALIZE_LIST_HEAD_VARIABLE(gMaps);
/**
Provides the controller-specific addresses required to access system memory from a
DMA bus master.
@param This The protocol 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.
**/
EFI_STATUS
EFIAPI
IoMmuMap (
IN EDKII_IOMMU_PROTOCOL *This,
IN EDKII_IOMMU_OPERATION Operation,
IN VOID *HostAddress,
IN OUT UINTN *NumberOfBytes,
OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
OUT VOID **Mapping
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS PhysicalAddress;
MAP_INFO *MapInfo;
EFI_PHYSICAL_ADDRESS DmaMemoryTop;
BOOLEAN NeedRemap;
if (NumberOfBytes == NULL || DeviceAddress == NULL ||
Mapping == NULL) {
DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));
return EFI_INVALID_PARAMETER;
}
DEBUG ((DEBUG_VERBOSE, "IoMmuMap: ==> 0x%08x - 0x%08x (%x)\n", HostAddress, *NumberOfBytes, Operation));
//
// Make sure that Operation is valid
//
if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_INVALID_PARAMETER));
return EFI_INVALID_PARAMETER;
}
NeedRemap = FALSE;
PhysicalAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;
DmaMemoryTop = DMA_MEMORY_TOP;
//
// Alignment check
//
if ((*NumberOfBytes != ALIGN_VALUE(*NumberOfBytes, SIZE_4KB)) ||
(PhysicalAddress != ALIGN_VALUE(PhysicalAddress, SIZE_4KB))) {
if ((Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||
(Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64)) {
//
// The input buffer might be a subset from IoMmuAllocateBuffer.
// Skip the check.
//
} else {
NeedRemap = TRUE;
}
}
if ((PhysicalAddress + *NumberOfBytes) >= DMA_MEMORY_TOP) {
NeedRemap = TRUE;
}
if (((Operation != EdkiiIoMmuOperationBusMasterRead64 &&
Operation != EdkiiIoMmuOperationBusMasterWrite64 &&
Operation != EdkiiIoMmuOperationBusMasterCommonBuffer64)) &&
((PhysicalAddress + *NumberOfBytes) > SIZE_4GB)) {
//
// If the root bridge or the device cannot handle performing DMA above
// 4GB but any part of the DMA transfer being mapped is above 4GB, then
// map the DMA transfer to a buffer below 4GB.
//
NeedRemap = TRUE;
DmaMemoryTop = MIN (DmaMemoryTop, SIZE_4GB - 1);
}
if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
if (NeedRemap) {
//
// Common Buffer operations can not be remapped. If the common buffer
// is above 4GB, then it is not possible to generate a mapping, so return
// an error.
//
DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_UNSUPPORTED));
return EFI_UNSUPPORTED;
}
}
//
// Allocate a MAP_INFO structure to remember the mapping when Unmap() is
// called later.
//
MapInfo = AllocatePool (sizeof (MAP_INFO));
if (MapInfo == NULL) {
*NumberOfBytes = 0;
DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", EFI_OUT_OF_RESOURCES));
return EFI_OUT_OF_RESOURCES;
}
//
// Initialize the MAP_INFO structure
//
MapInfo->Signature = MAP_INFO_SIGNATURE;
MapInfo->Operation = Operation;
MapInfo->NumberOfBytes = *NumberOfBytes;
MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
MapInfo->HostAddress = PhysicalAddress;
MapInfo->DeviceAddress = DmaMemoryTop;
//
// Allocate a buffer below 4GB to map the transfer to.
//
if (NeedRemap) {
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiBootServicesData,
MapInfo->NumberOfPages,
&MapInfo->DeviceAddress
);
if (EFI_ERROR (Status)) {
FreePool (MapInfo);
*NumberOfBytes = 0;
DEBUG ((DEBUG_ERROR, "IoMmuMap: %r\n", Status));
return Status;
}
//
// 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
);
}
} else {
MapInfo->DeviceAddress = MapInfo->HostAddress;
}
InsertTailList (&gMaps, &MapInfo->Link);
//
// The DeviceAddress is the address of the maped buffer below 4GB
//
*DeviceAddress = MapInfo->DeviceAddress;
//
// Return a pointer to the MAP_INFO structure in Mapping
//
*Mapping = MapInfo;
DEBUG ((DEBUG_VERBOSE, "IoMmuMap: 0x%08x - 0x%08x <==\n", *DeviceAddress, *Mapping));
return EFI_SUCCESS;
}
/**
Completes the Map() operation and releases any corresponding resources.
@param This The protocol 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.
**/
EFI_STATUS
EFIAPI
IoMmuUnmap (
IN EDKII_IOMMU_PROTOCOL *This,
IN VOID *Mapping
)
{
MAP_INFO *MapInfo;
LIST_ENTRY *Link;
DEBUG ((DEBUG_VERBOSE, "IoMmuUnmap: 0x%08x\n", Mapping));
if (Mapping == NULL) {
DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));
return EFI_INVALID_PARAMETER;
}
MapInfo = NULL;
for (Link = GetFirstNode (&gMaps)
; !IsNull (&gMaps, Link)
; Link = GetNextNode (&gMaps, Link)
) {
MapInfo = MAP_INFO_FROM_LINK (Link);
if (MapInfo == Mapping) {
break;
}
}
//
// Mapping is not a valid value returned by Map()
//
if (MapInfo != Mapping) {
DEBUG ((DEBUG_ERROR, "IoMmuUnmap: %r\n", EFI_INVALID_PARAMETER));
return EFI_INVALID_PARAMETER;
}
RemoveEntryList (&MapInfo->Link);
if (MapInfo->DeviceAddress != MapInfo->HostAddress) {
//
// 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
);
}
//
// Free the mapped buffer and the MAP_INFO structure.
//
gBS->FreePages (MapInfo->DeviceAddress, MapInfo->NumberOfPages);
}
FreePool (Mapping);
return EFI_SUCCESS;
}
/**
Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
OperationBusMasterCommonBuffer64 mapping.
@param This The protocol instance pointer.
@param Type This parameter is not used and must be ignored.
@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 and MEMORY_CACHED.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
**/
EFI_STATUS
EFIAPI
IoMmuAllocateBuffer (
IN EDKII_IOMMU_PROTOCOL *This,
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN OUT VOID **HostAddress,
IN UINT64 Attributes
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS PhysicalAddress;
DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: ==> 0x%08x\n", Pages));
//
// Validate Attributes
//
if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_UNSUPPORTED));
return EFI_UNSUPPORTED;
}
//
// Check for invalid inputs
//
if (HostAddress == NULL) {
DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));
return EFI_INVALID_PARAMETER;
}
//
// The only valid memory types are EfiBootServicesData and
// EfiRuntimeServicesData
//
if (MemoryType != EfiBootServicesData &&
MemoryType != EfiRuntimeServicesData) {
DEBUG ((DEBUG_ERROR, "IoMmuAllocateBuffer: %r\n", EFI_INVALID_PARAMETER));
return EFI_INVALID_PARAMETER;
}
PhysicalAddress = DMA_MEMORY_TOP;
if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {
//
// Limit allocations to memory below 4GB
//
PhysicalAddress = MIN (PhysicalAddress, SIZE_4GB - 1);
}
Status = gBS->AllocatePages (
AllocateMaxAddress,
MemoryType,
Pages,
&PhysicalAddress
);
if (!EFI_ERROR (Status)) {
*HostAddress = (VOID *) (UINTN) PhysicalAddress;
}
DEBUG ((DEBUG_VERBOSE, "IoMmuAllocateBuffer: 0x%08x <==\n", *HostAddress));
return Status;
}
/**
Frees memory that was allocated with AllocateBuffer().
@param This The protocol 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().
**/
EFI_STATUS
EFIAPI
IoMmuFreeBuffer (
IN EDKII_IOMMU_PROTOCOL *This,
IN UINTN Pages,
IN VOID *HostAddress
)
{
DEBUG ((DEBUG_VERBOSE, "IoMmuFreeBuffer: 0x%\n", Pages));
return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
}
/**
Get device information from mapping.
@param[in] Mapping The mapping.
@param[out] DeviceAddress The device address of the mapping.
@param[out] NumberOfPages The number of pages of the mapping.
@retval EFI_SUCCESS The device information is returned.
@retval EFI_INVALID_PARAMETER The mapping is invalid.
**/
EFI_STATUS
GetDeviceInfoFromMapping (
IN VOID *Mapping,
OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
OUT UINTN *NumberOfPages
)
{
MAP_INFO *MapInfo;
LIST_ENTRY *Link;
if (Mapping == NULL) {
return EFI_INVALID_PARAMETER;
}
MapInfo = NULL;
for (Link = GetFirstNode (&gMaps)
; !IsNull (&gMaps, Link)
; Link = GetNextNode (&gMaps, Link)
) {
MapInfo = MAP_INFO_FROM_LINK (Link);
if (MapInfo == Mapping) {
break;
}
}
//
// Mapping is not a valid value returned by Map()
//
if (MapInfo != Mapping) {
return EFI_INVALID_PARAMETER;
}
*DeviceAddress = MapInfo->DeviceAddress;
*NumberOfPages = MapInfo->NumberOfPages;
return EFI_SUCCESS;
}

View File

@@ -0,0 +1,503 @@
/** @file
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "DmaProtection.h"
EFI_ACPI_SDT_PROTOCOL *mAcpiSdt;
UINT64 mBelow4GMemoryLimit;
UINT64 mAbove4GMemoryLimit;
EDKII_PLATFORM_VTD_POLICY_PROTOCOL *mPlatformVTdPolicy;
/**
return the UEFI memory information.
@param[out] Below4GMemoryLimit The below 4GiB memory limit
@param[out] Above4GMemoryLimit The above 4GiB memory limit
**/
VOID
ReturnUefiMemoryMap (
OUT UINT64 *Below4GMemoryLimit,
OUT UINT64 *Above4GMemoryLimit
)
{
EFI_STATUS Status;
EFI_MEMORY_DESCRIPTOR *EfiMemoryMap;
EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd;
EFI_MEMORY_DESCRIPTOR *EfiEntry;
EFI_MEMORY_DESCRIPTOR *NextEfiEntry;
EFI_MEMORY_DESCRIPTOR TempEfiEntry;
UINTN EfiMemoryMapSize;
UINTN EfiMapKey;
UINTN EfiDescriptorSize;
UINT32 EfiDescriptorVersion;
UINT64 MemoryBlockLength;
*Below4GMemoryLimit = 0;
*Above4GMemoryLimit = 0;
//
// Get the EFI memory map.
//
EfiMemoryMapSize = 0;
EfiMemoryMap = NULL;
Status = gBS->GetMemoryMap (
&EfiMemoryMapSize,
EfiMemoryMap,
&EfiMapKey,
&EfiDescriptorSize,
&EfiDescriptorVersion
);
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
do {
//
// Use size returned back plus 1 descriptor for the AllocatePool.
// We don't just multiply by 2 since the "for" loop below terminates on
// EfiMemoryMapEnd which is dependent upon EfiMemoryMapSize. Otherwize
// we process bogus entries and create bogus E820 entries.
//
EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (EfiMemoryMapSize);
ASSERT (EfiMemoryMap != NULL);
Status = gBS->GetMemoryMap (
&EfiMemoryMapSize,
EfiMemoryMap,
&EfiMapKey,
&EfiDescriptorSize,
&EfiDescriptorVersion
);
if (EFI_ERROR (Status)) {
FreePool (EfiMemoryMap);
}
} while (Status == EFI_BUFFER_TOO_SMALL);
ASSERT_EFI_ERROR (Status);
//
// Sort memory map from low to high
//
EfiEntry = EfiMemoryMap;
NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
while (EfiEntry < EfiMemoryMapEnd) {
while (NextEfiEntry < EfiMemoryMapEnd) {
if (EfiEntry->PhysicalStart > NextEfiEntry->PhysicalStart) {
CopyMem (&TempEfiEntry, EfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
CopyMem (EfiEntry, NextEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
CopyMem (NextEfiEntry, &TempEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
}
NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (NextEfiEntry, EfiDescriptorSize);
}
EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
}
//
//
//
DEBUG ((DEBUG_INFO, "MemoryMap:\n"));
EfiEntry = EfiMemoryMap;
EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
while (EfiEntry < EfiMemoryMapEnd) {
MemoryBlockLength = (UINT64) (LShiftU64 (EfiEntry->NumberOfPages, 12));
DEBUG ((DEBUG_INFO, "Entry(0x%02x) 0x%016lx - 0x%016lx\n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->PhysicalStart + MemoryBlockLength));
switch (EfiEntry->Type) {
case EfiLoaderCode:
case EfiLoaderData:
case EfiBootServicesCode:
case EfiBootServicesData:
case EfiConventionalMemory:
case EfiRuntimeServicesCode:
case EfiRuntimeServicesData:
case EfiACPIReclaimMemory:
case EfiACPIMemoryNVS:
case EfiReservedMemoryType:
if ((EfiEntry->PhysicalStart + MemoryBlockLength) <= BASE_1MB) {
//
// Skip the memory block is under 1MB
//
} else if (EfiEntry->PhysicalStart >= BASE_4GB) {
if (*Above4GMemoryLimit < EfiEntry->PhysicalStart + MemoryBlockLength) {
*Above4GMemoryLimit = EfiEntry->PhysicalStart + MemoryBlockLength;
}
} else {
if (*Below4GMemoryLimit < EfiEntry->PhysicalStart + MemoryBlockLength) {
*Below4GMemoryLimit = EfiEntry->PhysicalStart + MemoryBlockLength;
}
}
break;
}
EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
}
FreePool (EfiMemoryMap);
DEBUG ((DEBUG_INFO, "Result:\n"));
DEBUG ((DEBUG_INFO, "Below4GMemoryLimit: 0x%016lx\n", *Below4GMemoryLimit));
DEBUG ((DEBUG_INFO, "Above4GMemoryLimit: 0x%016lx\n", *Above4GMemoryLimit));
return ;
}
/**
The scan bus callback function to always enable page attribute.
@param[in] Context The context of the callback.
@param[in] Segment The segment of the source.
@param[in] Bus The bus of the source.
@param[in] Device The device of the source.
@param[in] Function The function of the source.
@retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device.
**/
EFI_STATUS
EFIAPI
ScanBusCallbackAlwaysEnablePageAttribute (
IN VOID *Context,
IN UINT16 Segment,
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function
)
{
VTD_SOURCE_ID SourceId;
EFI_STATUS Status;
SourceId.Bits.Bus = Bus;
SourceId.Bits.Device = Device;
SourceId.Bits.Function = Function;
Status = AlwaysEnablePageAttribute (Segment, SourceId);
return Status;
}
/**
Always enable the VTd page attribute for the device in the DeviceScope.
@param[in] DeviceScope the input device scope data structure
@retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device in the device scope.
**/
EFI_STATUS
AlwaysEnablePageAttributeDeviceScope (
IN EDKII_PLATFORM_VTD_DEVICE_SCOPE *DeviceScope
)
{
UINT8 Bus;
UINT8 Device;
UINT8 Function;
VTD_SOURCE_ID SourceId;
UINT8 SecondaryBusNumber;
EFI_STATUS Status;
Status = GetPciBusDeviceFunction (DeviceScope->SegmentNumber, &DeviceScope->DeviceScope, &Bus, &Device, &Function);
if (DeviceScope->DeviceScope.Type == EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE) {
//
// Need scan the bridge and add all devices.
//
SecondaryBusNumber = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(DeviceScope->SegmentNumber, Bus, Device, Function, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
Status = ScanPciBus (NULL, DeviceScope->SegmentNumber, SecondaryBusNumber, ScanBusCallbackAlwaysEnablePageAttribute);
return Status;
} else {
SourceId.Bits.Bus = Bus;
SourceId.Bits.Device = Device;
SourceId.Bits.Function = Function;
Status = AlwaysEnablePageAttribute (DeviceScope->SegmentNumber, SourceId);
return Status;
}
}
/**
Always enable the VTd page attribute for the device matching DeviceId.
@param[in] PciDeviceId the input PCI device ID
@retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device matching DeviceId.
**/
EFI_STATUS
AlwaysEnablePageAttributePciDeviceId (
IN EDKII_PLATFORM_VTD_PCI_DEVICE_ID *PciDeviceId
)
{
UINTN VtdIndex;
UINTN PciIndex;
PCI_DEVICE_DATA *PciDeviceData;
EFI_STATUS Status;
for (VtdIndex = 0; VtdIndex < mVtdUnitNumber; VtdIndex++) {
for (PciIndex = 0; PciIndex < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceDataNumber; PciIndex++) {
PciDeviceData = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[PciIndex];
if (((PciDeviceId->VendorId == 0xFFFF) || (PciDeviceId->VendorId == PciDeviceData->PciDeviceId.VendorId)) &&
((PciDeviceId->DeviceId == 0xFFFF) || (PciDeviceId->DeviceId == PciDeviceData->PciDeviceId.DeviceId)) &&
((PciDeviceId->RevisionId == 0xFF) || (PciDeviceId->RevisionId == PciDeviceData->PciDeviceId.RevisionId)) &&
((PciDeviceId->SubsystemVendorId == 0xFFFF) || (PciDeviceId->SubsystemVendorId == PciDeviceData->PciDeviceId.SubsystemVendorId)) &&
((PciDeviceId->SubsystemDeviceId == 0xFFFF) || (PciDeviceId->SubsystemDeviceId == PciDeviceData->PciDeviceId.SubsystemDeviceId)) ) {
Status = AlwaysEnablePageAttribute (mVtdUnitInformation[VtdIndex].Segment, PciDeviceData->PciSourceId);
if (EFI_ERROR(Status)) {
continue;
}
}
}
}
return EFI_SUCCESS;
}
/**
Always enable the VTd page attribute for the device.
@param[in] DeviceInfo the exception device information
@retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device in the device info.
**/
EFI_STATUS
AlwaysEnablePageAttributeExceptionDeviceInfo (
IN EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO *DeviceInfo
)
{
switch (DeviceInfo->Type) {
case EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_DEVICE_SCOPE:
return AlwaysEnablePageAttributeDeviceScope ((VOID *)(DeviceInfo + 1));
case EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_PCI_DEVICE_ID:
return AlwaysEnablePageAttributePciDeviceId ((VOID *)(DeviceInfo + 1));
default:
return EFI_UNSUPPORTED;
}
}
/**
Initialize platform VTd policy.
**/
VOID
InitializePlatformVTdPolicy (
VOID
)
{
EFI_STATUS Status;
UINTN DeviceInfoCount;
VOID *DeviceInfo;
EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO *ThisDeviceInfo;
UINTN Index;
//
// It is optional.
//
Status = gBS->LocateProtocol (
&gEdkiiPlatformVTdPolicyProtocolGuid,
NULL,
(VOID **)&mPlatformVTdPolicy
);
if (!EFI_ERROR(Status)) {
DEBUG ((DEBUG_INFO, "InitializePlatformVTdPolicy\n"));
Status = mPlatformVTdPolicy->GetExceptionDeviceList (mPlatformVTdPolicy, &DeviceInfoCount, &DeviceInfo);
if (!EFI_ERROR(Status)) {
ThisDeviceInfo = DeviceInfo;
for (Index = 0; Index < DeviceInfoCount; Index++) {
if (ThisDeviceInfo->Type == EDKII_PLATFORM_VTD_EXCEPTION_DEVICE_INFO_TYPE_END) {
break;
}
AlwaysEnablePageAttributeExceptionDeviceInfo (ThisDeviceInfo);
ThisDeviceInfo = (VOID *)((UINTN)ThisDeviceInfo + ThisDeviceInfo->Length);
}
FreePool (DeviceInfo);
}
}
}
/**
Setup VTd engine.
**/
VOID
SetupVtd (
VOID
)
{
EFI_STATUS Status;
VOID *PciEnumerationComplete;
UINTN Index;
UINT64 Below4GMemoryLimit;
UINT64 Above4GMemoryLimit;
//
// PCI Enumeration must be done
//
Status = gBS->LocateProtocol (
&gEfiPciEnumerationCompleteProtocolGuid,
NULL,
&PciEnumerationComplete
);
ASSERT_EFI_ERROR (Status);
ReturnUefiMemoryMap (&Below4GMemoryLimit, &Above4GMemoryLimit);
Below4GMemoryLimit = ALIGN_VALUE_UP(Below4GMemoryLimit, SIZE_256MB);
DEBUG ((DEBUG_INFO, " Adjusted Below4GMemoryLimit: 0x%016lx\n", Below4GMemoryLimit));
mBelow4GMemoryLimit = Below4GMemoryLimit;
mAbove4GMemoryLimit = Above4GMemoryLimit;
//
// 1. setup
//
DEBUG ((DEBUG_INFO, "GetDmarAcpiTable\n"));
Status = GetDmarAcpiTable ();
if (EFI_ERROR (Status)) {
return;
}
DEBUG ((DEBUG_INFO, "ParseDmarAcpiTable\n"));
Status = ParseDmarAcpiTableDrhd ();
if (EFI_ERROR (Status)) {
return;
}
DEBUG ((DEBUG_INFO, "PrepareVtdConfig\n"));
PrepareVtdConfig ();
//
// 2. initialization
//
DEBUG ((DEBUG_INFO, "SetupTranslationTable\n"));
Status = SetupTranslationTable ();
if (EFI_ERROR (Status)) {
return;
}
InitializePlatformVTdPolicy ();
ParseDmarAcpiTableRmrr ();
for (Index = 0; Index < mVtdUnitNumber; Index++) {
DEBUG ((DEBUG_INFO,"VTD Unit %d (Segment: %04x)\n", Index, mVtdUnitInformation[Index].Segment));
if (mVtdUnitInformation[Index].ExtRootEntryTable != NULL) {
DumpDmarExtContextEntryTable (mVtdUnitInformation[Index].ExtRootEntryTable);
}
if (mVtdUnitInformation[Index].RootEntryTable != NULL) {
DumpDmarContextEntryTable (mVtdUnitInformation[Index].RootEntryTable);
}
}
//
// 3. enable
//
DEBUG ((DEBUG_INFO, "EnableDmar\n"));
Status = EnableDmar ();
if (EFI_ERROR (Status)) {
return;
}
DEBUG ((DEBUG_INFO, "DumpVtdRegs\n"));
DumpVtdRegsAll ();
}
/**
ACPI notification function.
@param[in] Table A pointer to the ACPI table header.
@param[in] Version The ACPI table's version.
@param[in] TableKey The table key for this ACPI table.
@retval EFI_SUCCESS The notification function is executed.
**/
EFI_STATUS
EFIAPI
AcpiNotificationFunc (
IN EFI_ACPI_SDT_HEADER *Table,
IN EFI_ACPI_TABLE_VERSION Version,
IN UINTN TableKey
)
{
if (Table->Signature == EFI_ACPI_4_0_DMA_REMAPPING_TABLE_SIGNATURE) {
DEBUG((DEBUG_INFO, "Vtd AcpiNotificationFunc\n"));
SetupVtd ();
}
return EFI_SUCCESS;
}
/**
Exit boot service callback function.
@param[in] Event The event handle.
@param[in] Context The event content.
**/
VOID
EFIAPI
OnExitBootServices (
IN EFI_EVENT Event,
IN VOID *Context
)
{
DEBUG ((DEBUG_INFO, "Vtd OnExitBootServices\n"));
DumpVtdRegsAll ();
if ((PcdGet8(PcdVTdPolicyPropertyMask) & BIT1) == 0) {
DisableDmar ();
DumpVtdRegsAll ();
}
}
/**
Legacy boot callback function.
@param[in] Event The event handle.
@param[in] Context The event content.
**/
VOID
EFIAPI
OnLegacyBoot (
EFI_EVENT Event,
VOID *Context
)
{
DEBUG ((DEBUG_INFO, "Vtd OnLegacyBoot\n"));
DumpVtdRegsAll ();
DisableDmar ();
DumpVtdRegsAll ();
}
/**
Initialize DMA protection.
**/
VOID
InitializeDmaProtection (
VOID
)
{
EFI_STATUS Status;
EFI_EVENT ExitBootServicesEvent;
EFI_EVENT LegacyBootEvent;
Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (VOID **) &mAcpiSdt);
ASSERT_EFI_ERROR (Status);
Status = mAcpiSdt->RegisterNotify (TRUE, AcpiNotificationFunc);
ASSERT_EFI_ERROR (Status);
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
OnExitBootServices,
NULL,
&gEfiEventExitBootServicesGuid,
&ExitBootServicesEvent
);
ASSERT_EFI_ERROR (Status);
Status = EfiCreateEventLegacyBootEx (
TPL_NOTIFY,
OnLegacyBoot,
NULL,
&LegacyBootEvent
);
ASSERT_EFI_ERROR (Status);
return ;
}

View File

@@ -0,0 +1,562 @@
/** @file
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#ifndef _DMAR_PROTECTION_H_
#define _DMAR_PROTECTION_H_
#include <Uefi.h>
#include <PiDxe.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/IoLib.h>
#include <Library/PciSegmentLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiLib.h>
#include <Library/CacheMaintenanceLib.h>
#include <Library/PerformanceLib.h>
#include <Library/PrintLib.h>
#include <Guid/EventGroup.h>
#include <Guid/Acpi.h>
#include <Protocol/DxeSmmReadyToLock.h>
#include <Protocol/PciRootBridgeIo.h>
#include <Protocol/PciIo.h>
#include <Protocol/PciEnumerationComplete.h>
#include <Protocol/AcpiSystemDescriptionTable.h>
#include <Protocol/PlatformVtdPolicy.h>
#include <Protocol/IoMmu.h>
#include <IndustryStandard/Pci.h>
#include <IndustryStandard/DmaRemappingReportingTable.h>
#include <IndustryStandard/Vtd.h>
#define VTD_64BITS_ADDRESS(Lo, Hi) (LShiftU64 (Lo, 12) | LShiftU64 (Hi, 32))
#define ALIGN_VALUE_UP(Value, Alignment) (((Value) + (Alignment) - 1) & (~((Alignment) - 1)))
#define ALIGN_VALUE_LOW(Value, Alignment) ((Value) & (~((Alignment) - 1)))
//
// This is the initial max PCI DATA number.
// The number may be enlarged later.
//
#define MAX_VTD_PCI_DATA_NUMBER 0x100
typedef struct {
UINT8 DeviceType;
VTD_SOURCE_ID PciSourceId;
EDKII_PLATFORM_VTD_PCI_DEVICE_ID PciDeviceId;
// for statistic analysis
UINTN AccessCount;
} PCI_DEVICE_DATA;
typedef struct {
BOOLEAN IncludeAllFlag;
UINTN PciDeviceDataNumber;
UINTN PciDeviceDataMaxNumber;
PCI_DEVICE_DATA *PciDeviceData;
} PCI_DEVICE_INFORMATION;
typedef struct {
UINTN VtdUnitBaseAddress;
UINT16 Segment;
VTD_CAP_REG CapReg;
VTD_ECAP_REG ECapReg;
VTD_ROOT_ENTRY *RootEntryTable;
VTD_EXT_ROOT_ENTRY *ExtRootEntryTable;
VTD_SECOND_LEVEL_PAGING_ENTRY *FixedSecondLevelPagingEntry;
BOOLEAN HasDirtyContext;
BOOLEAN HasDirtyPages;
PCI_DEVICE_INFORMATION PciDeviceInfo;
} VTD_UNIT_INFORMATION;
/**
The scan bus callback function.
It is called in PCI bus scan for each PCI device under the bus.
@param[in] Context The context of the callback.
@param[in] Segment The segment of the source.
@param[in] Bus The bus of the source.
@param[in] Device The device of the source.
@param[in] Function The function of the source.
@retval EFI_SUCCESS The specific PCI device is processed in the callback.
**/
typedef
EFI_STATUS
(EFIAPI *SCAN_BUS_FUNC_CALLBACK_FUNC) (
IN VOID *Context,
IN UINT16 Segment,
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function
);
extern EFI_ACPI_DMAR_HEADER *mAcpiDmarTable;
extern UINT64 mVtdHostAddressWidthMask;
extern UINTN mVtdUnitNumber;
extern VTD_UNIT_INFORMATION *mVtdUnitInformation;
extern UINT64 mBelow4GMemoryLimit;
extern UINT64 mAbove4GMemoryLimit;
extern EDKII_PLATFORM_VTD_POLICY_PROTOCOL *mPlatformVTdPolicy;
/**
Prepare VTD configuration.
**/
VOID
PrepareVtdConfig (
VOID
);
/**
Setup VTd translation table.
@retval EFI_SUCCESS Setup translation table successfully.
@retval EFI_OUT_OF_RESOURCE Setup translation table fail.
**/
EFI_STATUS
SetupTranslationTable (
VOID
);
/**
Enable DMAR translation.
@retval EFI_SUCCESS DMAR translation is enabled.
@retval EFI_DEVICE_ERROR DMAR translation is not enabled.
**/
EFI_STATUS
EnableDmar (
VOID
);
/**
Disable DMAR translation.
@retval EFI_SUCCESS DMAR translation is disabled.
@retval EFI_DEVICE_ERROR DMAR translation is not disabled.
**/
EFI_STATUS
DisableDmar (
VOID
);
/**
Invalid VTd global IOTLB.
@param[in] VtdIndex The index of VTd engine.
@retval EFI_SUCCESS VTd global IOTLB is invalidated.
@retval EFI_DEVICE_ERROR VTd global IOTLB is not invalidated.
**/
EFI_STATUS
InvalidateVtdIOTLBGlobal (
IN UINTN VtdIndex
);
/**
Dump VTd registers.
@param[in] VtdIndex The index of VTd engine.
**/
VOID
DumpVtdRegs (
IN UINTN VtdIndex
);
/**
Dump VTd registers for all VTd engine.
**/
VOID
DumpVtdRegsAll (
VOID
);
/**
Dump VTd capability registers.
@param[in] CapReg The capability register.
**/
VOID
DumpVtdCapRegs (
IN VTD_CAP_REG *CapReg
);
/**
Dump VTd extended capability registers.
@param[in] ECapReg The extended capability register.
**/
VOID
DumpVtdECapRegs (
IN VTD_ECAP_REG *ECapReg
);
/**
Register PCI device to VTd engine.
@param[in] VtdIndex The index of VTd engine.
@param[in] Segment The segment of the source.
@param[in] SourceId The SourceId of the source.
@param[in] DeviceType The DMAR device scope type.
@param[in] CheckExist TRUE: ERROR will be returned if the PCI device is already registered.
FALSE: SUCCESS will be returned if the PCI device is registered.
@retval EFI_SUCCESS The PCI device is registered.
@retval EFI_OUT_OF_RESOURCES No enough resource to register a new PCI device.
@retval EFI_ALREADY_STARTED The device is already registered.
**/
EFI_STATUS
RegisterPciDevice (
IN UINTN VtdIndex,
IN UINT16 Segment,
IN VTD_SOURCE_ID SourceId,
IN UINT8 DeviceType,
IN BOOLEAN CheckExist
);
/**
The scan bus callback function to always enable page attribute.
@param[in] Context The context of the callback.
@param[in] Segment The segment of the source.
@param[in] Bus The bus of the source.
@param[in] Device The device of the source.
@param[in] Function The function of the source.
@retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device.
**/
EFI_STATUS
EFIAPI
ScanBusCallbackRegisterPciDevice (
IN VOID *Context,
IN UINT16 Segment,
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function
);
/**
Scan PCI bus and invoke callback function for each PCI devices under the bus.
@param[in] Context The context of the callback function.
@param[in] Segment The segment of the source.
@param[in] Bus The bus of the source.
@param[in] Callback The callback function in PCI scan.
@retval EFI_SUCCESS The PCI devices under the bus are scaned.
**/
EFI_STATUS
ScanPciBus (
IN VOID *Context,
IN UINT16 Segment,
IN UINT8 Bus,
IN SCAN_BUS_FUNC_CALLBACK_FUNC Callback
);
/**
Dump the PCI device information managed by this VTd engine.
@param[in] VtdIndex The index of VTd engine.
**/
VOID
DumpPciDeviceInfo (
IN UINTN VtdIndex
);
/**
Find the VTd index by the Segment and SourceId.
@param[in] Segment The segment of the source.
@param[in] SourceId The SourceId of the source.
@param[out] ExtContextEntry The ExtContextEntry of the source.
@param[out] ContextEntry The ContextEntry of the source.
@return The index of the VTd engine.
@retval (UINTN)-1 The VTd engine is not found.
**/
UINTN
FindVtdIndexByPciDevice (
IN UINT16 Segment,
IN VTD_SOURCE_ID SourceId,
OUT VTD_EXT_CONTEXT_ENTRY **ExtContextEntry,
OUT VTD_CONTEXT_ENTRY **ContextEntry
);
/**
Get the DMAR ACPI table.
@retval EFI_SUCCESS The DMAR ACPI table is got.
@retval EFI_NOT_FOUND The DMAR ACPI table is not found.
**/
EFI_STATUS
GetDmarAcpiTable (
VOID
);
/**
Parse DMAR DRHD table.
@return EFI_SUCCESS The DMAR DRHD table is parsed.
**/
EFI_STATUS
ParseDmarAcpiTableDrhd (
VOID
);
/**
Parse DMAR RMRR table.
@return EFI_SUCCESS The DMAR RMRR table is parsed.
**/
EFI_STATUS
ParseDmarAcpiTableRmrr (
VOID
);
/**
Dump DMAR context entry table.
@param[in] RootEntry DMAR root entry.
**/
VOID
DumpDmarContextEntryTable (
IN VTD_ROOT_ENTRY *RootEntry
);
/**
Dump DMAR extended context entry table.
@param[in] ExtRootEntry DMAR extended root entry.
**/
VOID
DumpDmarExtContextEntryTable (
IN VTD_EXT_ROOT_ENTRY *ExtRootEntry
);
/**
Dump DMAR second level paging entry.
@param[in] SecondLevelPagingEntry The second level paging entry.
**/
VOID
DumpSecondLevelPagingEntry (
IN VOID *SecondLevelPagingEntry
);
/**
Set VTd attribute for a system memory.
@param[in] VtdIndex The index used to identify a VTd engine.
@param[in] DomainIdentifier The domain ID of the source.
@param[in] SecondLevelPagingEntry The second level paging entry in VTd table for the device.
@param[in] BaseAddress The base of device memory address to be used as the DMA memory.
@param[in] Length The length of device memory address to be used as the DMA memory.
@param[in] IoMmuAccess The IOMMU access.
@retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by BaseAddress and Length.
@retval EFI_INVALID_PARAMETER BaseAddress is not IoMmu Page size aligned.
@retval EFI_INVALID_PARAMETER Length is not IoMmu Page size aligned.
@retval EFI_INVALID_PARAMETER Length is 0.
@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 BaseAddress and Length.
@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.
**/
EFI_STATUS
SetPageAttribute (
IN UINTN VtdIndex,
IN UINT16 DomainIdentifier,
IN VTD_SECOND_LEVEL_PAGING_ENTRY *SecondLevelPagingEntry,
IN UINT64 BaseAddress,
IN UINT64 Length,
IN UINT64 IoMmuAccess
);
/**
Set VTd attribute for a system memory.
@param[in] Segment The Segment used to identify a VTd engine.
@param[in] SourceId The SourceId used to identify a VTd engine and table entry.
@param[in] BaseAddress The base of device memory address to be used as the DMA memory.
@param[in] Length The length of device memory address to be used as the DMA memory.
@param[in] IoMmuAccess The IOMMU access.
@retval EFI_SUCCESS The IoMmuAccess is set for the memory range specified by BaseAddress and Length.
@retval EFI_INVALID_PARAMETER BaseAddress is not IoMmu Page size aligned.
@retval EFI_INVALID_PARAMETER Length is not IoMmu Page size aligned.
@retval EFI_INVALID_PARAMETER Length is 0.
@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 BaseAddress and Length.
@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.
**/
EFI_STATUS
SetAccessAttribute (
IN UINT16 Segment,
IN VTD_SOURCE_ID SourceId,
IN UINT64 BaseAddress,
IN UINT64 Length,
IN UINT64 IoMmuAccess
);
/**
Return the index of PCI data.
@param[in] VtdIndex The index used to identify a VTd engine.
@param[in] Segment The Segment used to identify a VTd engine.
@param[in] SourceId The SourceId used to identify a VTd engine and table entry.
@return The index of the PCI data.
@retval (UINTN)-1 The PCI data is not found.
**/
UINTN
GetPciDataIndex (
IN UINTN VtdIndex,
IN UINT16 Segment,
IN VTD_SOURCE_ID SourceId
);
/**
Dump VTd registers if there is error.
**/
VOID
DumpVtdIfError (
VOID
);
/**
Initialize platform VTd policy.
**/
VOID
InitializePlatformVTdPolicy (
VOID
);
/**
Always enable the VTd page attribute for the device.
@param[in] Segment The Segment used to identify a VTd engine.
@param[in] SourceId The SourceId used to identify a VTd engine and table entry.
@retval EFI_SUCCESS The VTd entry is updated to always enable all DMA access for the specific device.
**/
EFI_STATUS
AlwaysEnablePageAttribute (
IN UINT16 Segment,
IN VTD_SOURCE_ID SourceId
);
/**
Convert the DeviceHandle to SourceId and Segment.
@param[in] DeviceHandle The device who initiates the DMA access request.
@param[out] Segment The Segment used to identify a VTd engine.
@param[out] SourceId The SourceId used to identify a VTd engine and table entry.
@retval EFI_SUCCESS The Segment and SourceId are returned.
@retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.
@retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.
**/
EFI_STATUS
DeviceHandleToSourceId (
IN EFI_HANDLE DeviceHandle,
OUT UINT16 *Segment,
OUT VTD_SOURCE_ID *SourceId
);
/**
Get device information from mapping.
@param[in] Mapping The mapping.
@param[out] DeviceAddress The device address of the mapping.
@param[out] NumberOfPages The number of pages of the mapping.
@retval EFI_SUCCESS The device information is returned.
@retval EFI_INVALID_PARAMETER The mapping is invalid.
**/
EFI_STATUS
GetDeviceInfoFromMapping (
IN VOID *Mapping,
OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
OUT UINTN *NumberOfPages
);
/**
Initialize DMA protection.
**/
VOID
InitializeDmaProtection (
VOID
);
/**
Allocate zero pages.
@param[in] Pages the number of pages.
@return the page address.
@retval NULL No resource to allocate pages.
**/
VOID *
EFIAPI
AllocateZeroPages (
IN UINTN Pages
);
/**
Flush VTD page table and context table memory.
This action is to make sure the IOMMU engine can get final data in memory.
@param[in] VtdIndex The index used to identify a VTd engine.
@param[in] Base The base address of memory to be flushed.
@param[in] Size The size of memory in bytes to be flushed.
**/
VOID
FlushPageTableMemory (
IN UINTN VtdIndex,
IN UINTN Base,
IN UINTN Size
);
/**
Get PCI device information from DMAR DevScopeEntry.
@param[in] Segment The segment number.
@param[in] DmarDevScopeEntry DMAR DevScopeEntry
@param[out] Bus The bus number.
@param[out] Device The device number.
@param[out] Function The function number.
@retval EFI_SUCCESS The PCI device information is returned.
**/
EFI_STATUS
GetPciBusDeviceFunction (
IN UINT16 Segment,
IN EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *DmarDevScopeEntry,
OUT UINT8 *Bus,
OUT UINT8 *Device,
OUT UINT8 *Function
);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,370 @@
/** @file
Intel VTd driver.
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <PiDxe.h>
#include <Protocol/IoMmu.h>
#include <Protocol/PciIo.h>
#include <Library/IoLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include "DmaProtection.h"
/**
Provides the controller-specific addresses required to access system memory from a
DMA bus master.
@param This The protocol 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.
**/
EFI_STATUS
EFIAPI
IoMmuMap (
IN EDKII_IOMMU_PROTOCOL *This,
IN EDKII_IOMMU_OPERATION Operation,
IN VOID *HostAddress,
IN OUT UINTN *NumberOfBytes,
OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
OUT VOID **Mapping
);
/**
Completes the Map() operation and releases any corresponding resources.
@param This The protocol 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.
**/
EFI_STATUS
EFIAPI
IoMmuUnmap (
IN EDKII_IOMMU_PROTOCOL *This,
IN VOID *Mapping
);
/**
Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
OperationBusMasterCommonBuffer64 mapping.
@param This The protocol instance pointer.
@param Type This parameter is not used and must be ignored.
@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 and MEMORY_CACHED.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
**/
EFI_STATUS
EFIAPI
IoMmuAllocateBuffer (
IN EDKII_IOMMU_PROTOCOL *This,
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN Pages,
IN OUT VOID **HostAddress,
IN UINT64 Attributes
);
/**
Frees memory that was allocated with AllocateBuffer().
@param This The protocol 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().
**/
EFI_STATUS
EFIAPI
IoMmuFreeBuffer (
IN EDKII_IOMMU_PROTOCOL *This,
IN UINTN Pages,
IN VOID *HostAddress
);
/**
Convert the DeviceHandle to SourceId and Segment.
@param[in] DeviceHandle The device who initiates the DMA access request.
@param[out] Segment The Segment used to identify a VTd engine.
@param[out] SourceId The SourceId used to identify a VTd engine and table entry.
@retval EFI_SUCCESS The Segment and SourceId are returned.
@retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.
@retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.
**/
EFI_STATUS
DeviceHandleToSourceId (
IN EFI_HANDLE DeviceHandle,
OUT UINT16 *Segment,
OUT VTD_SOURCE_ID *SourceId
)
{
EFI_PCI_IO_PROTOCOL *PciIo;
UINTN Seg;
UINTN Bus;
UINTN Dev;
UINTN Func;
EFI_STATUS Status;
EDKII_PLATFORM_VTD_DEVICE_INFO DeviceInfo;
Status = EFI_NOT_FOUND;
if (mPlatformVTdPolicy != NULL) {
Status = mPlatformVTdPolicy->GetDeviceId (mPlatformVTdPolicy, DeviceHandle, &DeviceInfo);
if (!EFI_ERROR(Status)) {
*Segment = DeviceInfo.Segment;
*SourceId = DeviceInfo.SourceId;
return EFI_SUCCESS;
}
}
Status = gBS->HandleProtocol (DeviceHandle, &gEfiPciIoProtocolGuid, (VOID **)&PciIo);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
Status = PciIo->GetLocation (PciIo, &Seg, &Bus, &Dev, &Func);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
*Segment = (UINT16)Seg;
SourceId->Bits.Bus = (UINT8)Bus;
SourceId->Bits.Device = (UINT8)Dev;
SourceId->Bits.Function = (UINT8)Func;
return EFI_SUCCESS;
}
/**
Set IOMMU attribute for a system memory.
If the IOMMU protocol 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).
The DeviceHandle is used to identify which device submits the request.
The IOMMU implementation need translate the device path to an IOMMU device ID,
and set IOMMU hardware register accordingly.
1) DeviceHandle can be a standard PCI device.
The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
After the memory is used, the memory need set 0 to keep it being protected.
2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
@param[in] This The protocol instance pointer.
@param[in] DeviceHandle The device who initiates the DMA access request.
@param[in] DeviceAddress The base of device memory address to be used as the DMA memory.
@param[in] Length The length of device memory address to be used as the DMA memory.
@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 DeviceHandle is an invalid handle.
@retval EFI_INVALID_PARAMETER DeviceAddress is not IoMmu Page size aligned.
@retval EFI_INVALID_PARAMETER Length is not IoMmu Page size aligned.
@retval EFI_INVALID_PARAMETER Length is 0.
@retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination of access.
@retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.
@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 DeviceAddress and Length.
@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.
**/
EFI_STATUS
VTdSetAttribute (
IN EDKII_IOMMU_PROTOCOL *This,
IN EFI_HANDLE DeviceHandle,
IN EFI_PHYSICAL_ADDRESS DeviceAddress,
IN UINT64 Length,
IN UINT64 IoMmuAccess
)
{
EFI_STATUS Status;
UINT16 Segment;
VTD_SOURCE_ID SourceId;
CHAR8 PerfToken[sizeof("VTD(S0000.B00.D00.F00)")];
UINT32 Identifier;
DumpVtdIfError ();
Status = DeviceHandleToSourceId (DeviceHandle, &Segment, &SourceId);
if (EFI_ERROR(Status)) {
return Status;
}
DEBUG ((DEBUG_VERBOSE, "IoMmuSetAttribute: "));
DEBUG ((DEBUG_VERBOSE, "PCI(S%x.B%x.D%x.F%x) ", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
DEBUG ((DEBUG_VERBOSE, "(0x%lx~0x%lx) - %lx\n", DeviceAddress, Length, IoMmuAccess));
PERF_CODE (
AsciiSPrint (PerfToken, sizeof(PerfToken), "S%04xB%02xD%02xF%01x", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function);
Identifier = (Segment << 16) | SourceId.Uint16;
PERF_START_EX (gImageHandle, PerfToken, "IntelVTD", 0, Identifier);
);
Status = SetAccessAttribute (Segment, SourceId, DeviceAddress, Length, IoMmuAccess);
PERF_CODE (
Identifier = (Segment << 16) | SourceId.Uint16;
PERF_END_EX (gImageHandle, PerfToken, "IntelVTD", 0, Identifier);
);
return Status;
}
/**
Set IOMMU attribute for a system memory.
If the IOMMU protocol 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).
The DeviceHandle is used to identify which device submits the request.
The IOMMU implementation need translate the device path to an IOMMU device ID,
and set IOMMU hardware register accordingly.
1) DeviceHandle can be a standard PCI device.
The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
The memory for BusMasterCommonBuffer need set EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
After the memory is used, the memory need set 0 to keep it being protected.
2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or EDKII_IOMMU_ACCESS_WRITE.
@param[in] This The protocol 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 DeviceHandle is an invalid handle.
@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 DeviceHandle is unknown by the IOMMU.
@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.
**/
EFI_STATUS
EFIAPI
IoMmuSetAttribute (
IN EDKII_IOMMU_PROTOCOL *This,
IN EFI_HANDLE DeviceHandle,
IN VOID *Mapping,
IN UINT64 IoMmuAccess
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS DeviceAddress;
UINTN NumberOfPages;
Status = GetDeviceInfoFromMapping (Mapping, &DeviceAddress, &NumberOfPages);
if (EFI_ERROR(Status)) {
return Status;
}
Status = VTdSetAttribute (
This,
DeviceHandle,
DeviceAddress,
EFI_PAGES_TO_SIZE(NumberOfPages),
IoMmuAccess
);
return Status;
}
EDKII_IOMMU_PROTOCOL mIntelVTd = {
EDKII_IOMMU_PROTOCOL_REVISION,
IoMmuSetAttribute,
IoMmuMap,
IoMmuUnmap,
IoMmuAllocateBuffer,
IoMmuFreeBuffer,
};
/**
Initialize the VTd driver.
@param[in] ImageHandle ImageHandle of the loaded driver
@param[in] SystemTable Pointer to the System Table
@retval EFI_SUCCESS The Protocol is installed.
@retval EFI_OUT_OF_RESOURCES Not enough resources available to initialize driver.
@retval EFI_DEVICE_ERROR A device error occurred attempting to initialize the driver.
**/
EFI_STATUS
EFIAPI
IntelVTdInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
if ((PcdGet8(PcdVTdPolicyPropertyMask) & BIT0) == 0) {
return EFI_UNSUPPORTED;
}
InitializeDmaProtection ();
Handle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&Handle,
&gEdkiiIoMmuProtocolGuid, &mIntelVTd,
NULL
);
ASSERT_EFI_ERROR (Status);
return Status;
}

View File

@@ -0,0 +1,85 @@
## @file
# Intel VTd DXE Driver.
#
# This driver initializes VTd engine based upon DMAR ACPI tables
# and provide DMA protection to PCI or ACPI device.
#
# Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = IntelVTdDxe
MODULE_UNI_FILE = IntelVTdDxe.uni
FILE_GUID = 987555D6-595D-4CFA-B895-59B89368BD4D
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = IntelVTdInitialize
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
#
#
[Sources]
IntelVTdDxe.c
BmDma.c
DmaProtection.c
DmaProtection.h
DmarAcpiTable.c
PciInfo.c
TranslationTable.c
TranslationTableEx.c
VtdReg.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
IntelSiliconPkg/IntelSiliconPkg.dec
[LibraryClasses]
DebugLib
UefiDriverEntryPoint
UefiBootServicesTableLib
BaseLib
IoLib
PciSegmentLib
BaseMemoryLib
MemoryAllocationLib
UefiLib
CacheMaintenanceLib
PerformanceLib
PrintLib
[Guids]
gEfiEventExitBootServicesGuid ## CONSUMES ## Event
gEfiAcpi20TableGuid ## CONSUMES ## SystemTable
gEfiAcpiTableGuid ## CONSUMES ## SystemTable
[Protocols]
gEdkiiIoMmuProtocolGuid ## PRODUCES
gEfiAcpiSdtProtocolGuid ## CONSUMES
gEfiPciIoProtocolGuid ## CONSUMES
gEfiPciEnumerationCompleteProtocolGuid ## CONSUMES
gEdkiiPlatformVTdPolicyProtocolGuid ## SOMETIMES_CONSUMES
[Pcd]
gIntelSiliconPkgTokenSpaceGuid.PcdVTdPolicyPropertyMask ## CONSUMES
[Depex]
gEfiPciRootBridgeIoProtocolGuid AND
gEfiAcpiSdtProtocolGuid
[UserExtensions.TianoCore."ExtraFiles"]
IntelVTdDxeExtra.uni

View File

@@ -0,0 +1,20 @@
// /** @file
// IntelVTdDxe Module Localized Abstract and Description Content
//
// Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
//
// This program and the accompanying materials are
// licensed and made available under the terms and conditions of the BSD License
// which accompanies this distribution. The full text of the license may be found at
// http://opensource.org/licenses/bsd-license.php
//
// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Intel VTd DXE Driver."
#string STR_MODULE_DESCRIPTION #language en-US "This driver initializes VTd engine based upon DMAR ACPI tables and provide DMA protection to PCI or ACPI device."

View File

@@ -0,0 +1,20 @@
// /** @file
// IntelVTdDxe Localized Strings and Content
//
// Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
//
// This program and the accompanying materials are
// licensed and made available under the terms and conditions of the BSD License
// which accompanies this distribution. The full text of the license may be found at
// http://opensource.org/licenses/bsd-license.php
//
// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
//
// **/
#string STR_PROPERTIES_MODULE_NAME
#language en-US
"Intel VTd DXE Driver"

View File

@@ -0,0 +1,369 @@
/** @file
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "DmaProtection.h"
/**
Return the index of PCI data.
@param[in] VtdIndex The index used to identify a VTd engine.
@param[in] Segment The Segment used to identify a VTd engine.
@param[in] SourceId The SourceId used to identify a VTd engine and table entry.
@return The index of the PCI data.
@retval (UINTN)-1 The PCI data is not found.
**/
UINTN
GetPciDataIndex (
IN UINTN VtdIndex,
IN UINT16 Segment,
IN VTD_SOURCE_ID SourceId
)
{
UINTN Index;
VTD_SOURCE_ID *PciSourceId;
if (Segment != mVtdUnitInformation[VtdIndex].Segment) {
return (UINTN)-1;
}
for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceDataNumber; Index++) {
PciSourceId = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[Index].PciSourceId;
if ((PciSourceId->Bits.Bus == SourceId.Bits.Bus) &&
(PciSourceId->Bits.Device == SourceId.Bits.Device) &&
(PciSourceId->Bits.Function == SourceId.Bits.Function) ) {
return Index;
}
}
return (UINTN)-1;
}
/**
Register PCI device to VTd engine.
@param[in] VtdIndex The index of VTd engine.
@param[in] Segment The segment of the source.
@param[in] SourceId The SourceId of the source.
@param[in] DeviceType The DMAR device scope type.
@param[in] CheckExist TRUE: ERROR will be returned if the PCI device is already registered.
FALSE: SUCCESS will be returned if the PCI device is registered.
@retval EFI_SUCCESS The PCI device is registered.
@retval EFI_OUT_OF_RESOURCES No enough resource to register a new PCI device.
@retval EFI_ALREADY_STARTED The device is already registered.
**/
EFI_STATUS
RegisterPciDevice (
IN UINTN VtdIndex,
IN UINT16 Segment,
IN VTD_SOURCE_ID SourceId,
IN UINT8 DeviceType,
IN BOOLEAN CheckExist
)
{
PCI_DEVICE_INFORMATION *PciDeviceInfo;
VTD_SOURCE_ID *PciSourceId;
UINTN PciDataIndex;
UINTN Index;
PCI_DEVICE_DATA *NewPciDeviceData;
EDKII_PLATFORM_VTD_PCI_DEVICE_ID *PciDeviceId;
PciDeviceInfo = &mVtdUnitInformation[VtdIndex].PciDeviceInfo;
if (PciDeviceInfo->IncludeAllFlag) {
//
// Do not register device in other VTD Unit
//
for (Index = 0; Index < VtdIndex; Index++) {
PciDataIndex = GetPciDataIndex (Index, Segment, SourceId);
if (PciDataIndex != (UINTN)-1) {
DEBUG ((DEBUG_INFO, " RegisterPciDevice: PCI S%04x B%02x D%02x F%02x already registered by Other Vtd(%d)\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function, Index));
return EFI_SUCCESS;
}
}
}
PciDataIndex = GetPciDataIndex (VtdIndex, Segment, SourceId);
if (PciDataIndex == (UINTN)-1) {
//
// Register new
//
if (PciDeviceInfo->PciDeviceDataNumber >= PciDeviceInfo->PciDeviceDataMaxNumber) {
//
// Reallocate
//
NewPciDeviceData = AllocateZeroPool (sizeof(*NewPciDeviceData) * (PciDeviceInfo->PciDeviceDataMaxNumber + MAX_VTD_PCI_DATA_NUMBER));
if (NewPciDeviceData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
PciDeviceInfo->PciDeviceDataMaxNumber += MAX_VTD_PCI_DATA_NUMBER;
if (PciDeviceInfo->PciDeviceData != NULL) {
CopyMem (NewPciDeviceData, PciDeviceInfo->PciDeviceData, sizeof(*NewPciDeviceData) * PciDeviceInfo->PciDeviceDataNumber);
FreePool (PciDeviceInfo->PciDeviceData);
}
PciDeviceInfo->PciDeviceData = NewPciDeviceData;
}
ASSERT (PciDeviceInfo->PciDeviceDataNumber < PciDeviceInfo->PciDeviceDataMaxNumber);
PciSourceId = &PciDeviceInfo->PciDeviceData[PciDeviceInfo->PciDeviceDataNumber].PciSourceId;
PciSourceId->Bits.Bus = SourceId.Bits.Bus;
PciSourceId->Bits.Device = SourceId.Bits.Device;
PciSourceId->Bits.Function = SourceId.Bits.Function;
DEBUG ((DEBUG_INFO, " RegisterPciDevice: PCI S%04x B%02x D%02x F%02x", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
PciDeviceId = &PciDeviceInfo->PciDeviceData[PciDeviceInfo->PciDeviceDataNumber].PciDeviceId;
if ((DeviceType == EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT) ||
(DeviceType == EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE)) {
PciDeviceId->VendorId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS(Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function, PCI_VENDOR_ID_OFFSET));
PciDeviceId->DeviceId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS(Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function, PCI_DEVICE_ID_OFFSET));
PciDeviceId->RevisionId = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function, PCI_REVISION_ID_OFFSET));
DEBUG ((DEBUG_INFO, " (%04x:%04x:%02x", PciDeviceId->VendorId, PciDeviceId->DeviceId, PciDeviceId->RevisionId));
if (DeviceType == EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT) {
PciDeviceId->SubsystemVendorId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS(Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function, PCI_SUBSYSTEM_VENDOR_ID_OFFSET));
PciDeviceId->SubsystemDeviceId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS(Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function, PCI_SUBSYSTEM_ID_OFFSET));
DEBUG ((DEBUG_INFO, ":%04x:%04x", PciDeviceId->SubsystemVendorId, PciDeviceId->SubsystemDeviceId));
}
DEBUG ((DEBUG_INFO, ")"));
}
PciDeviceInfo->PciDeviceData[PciDeviceInfo->PciDeviceDataNumber].DeviceType = DeviceType;
if ((DeviceType != EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT) &&
(DeviceType != EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE)) {
DEBUG ((DEBUG_INFO, " (*)"));
}
DEBUG ((DEBUG_INFO, "\n"));
PciDeviceInfo->PciDeviceDataNumber++;
} else {
if (CheckExist) {
DEBUG ((DEBUG_INFO, " RegisterPciDevice: PCI S%04x B%02x D%02x F%02x already registered\n", Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
return EFI_ALREADY_STARTED;
}
}
return EFI_SUCCESS;
}
/**
The scan bus callback function to register PCI device.
@param[in] Context The context of the callback.
@param[in] Segment The segment of the source.
@param[in] Bus The bus of the source.
@param[in] Device The device of the source.
@param[in] Function The function of the source.
@retval EFI_SUCCESS The PCI device is registered.
**/
EFI_STATUS
EFIAPI
ScanBusCallbackRegisterPciDevice (
IN VOID *Context,
IN UINT16 Segment,
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function
)
{
VTD_SOURCE_ID SourceId;
UINTN VtdIndex;
UINT8 BaseClass;
UINT8 SubClass;
UINT8 DeviceType;
EFI_STATUS Status;
VtdIndex = (UINTN)Context;
SourceId.Bits.Bus = Bus;
SourceId.Bits.Device = Device;
SourceId.Bits.Function = Function;
DeviceType = EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT;
BaseClass = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_CLASSCODE_OFFSET + 2));
if (BaseClass == PCI_CLASS_BRIDGE) {
SubClass = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_CLASSCODE_OFFSET + 1));
if (SubClass == PCI_CLASS_BRIDGE_P2P) {
DeviceType = EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE;
}
}
Status = RegisterPciDevice (VtdIndex, Segment, SourceId, DeviceType, FALSE);
return Status;
}
/**
Scan PCI bus and invoke callback function for each PCI devices under the bus.
@param[in] Context The context of the callback function.
@param[in] Segment The segment of the source.
@param[in] Bus The bus of the source.
@param[in] Callback The callback function in PCI scan.
@retval EFI_SUCCESS The PCI devices under the bus are scaned.
**/
EFI_STATUS
ScanPciBus (
IN VOID *Context,
IN UINT16 Segment,
IN UINT8 Bus,
IN SCAN_BUS_FUNC_CALLBACK_FUNC Callback
)
{
UINT8 Device;
UINT8 Function;
UINT8 SecondaryBusNumber;
UINT8 HeaderType;
UINT8 BaseClass;
UINT8 SubClass;
UINT32 MaxFunction;
UINT16 VendorID;
UINT16 DeviceID;
EFI_STATUS Status;
// Scan the PCI bus for devices
for (Device = 0; Device < PCI_MAX_DEVICE + 1; Device++) {
HeaderType = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, 0, PCI_HEADER_TYPE_OFFSET));
MaxFunction = PCI_MAX_FUNC + 1;
if ((HeaderType & HEADER_TYPE_MULTI_FUNCTION) == 0x00) {
MaxFunction = 1;
}
for (Function = 0; Function < MaxFunction; Function++) {
VendorID = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_VENDOR_ID_OFFSET));
DeviceID = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_DEVICE_ID_OFFSET));
if (VendorID == 0xFFFF && DeviceID == 0xFFFF) {
continue;
}
Status = Callback (Context, Segment, Bus, Device, Function);
if (EFI_ERROR (Status)) {
return Status;
}
BaseClass = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_CLASSCODE_OFFSET + 2));
if (BaseClass == PCI_CLASS_BRIDGE) {
SubClass = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_CLASSCODE_OFFSET + 1));
if (SubClass == PCI_CLASS_BRIDGE_P2P) {
SecondaryBusNumber = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS(Segment, Bus, Device, Function, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
DEBUG ((DEBUG_INFO," ScanPciBus: PCI bridge S%04x B%02x D%02x F%02x (SecondBus:%02x)\n", Segment, Bus, Device, Function, SecondaryBusNumber));
if (SecondaryBusNumber != 0) {
Status = ScanPciBus (Context, Segment, SecondaryBusNumber, Callback);
if (EFI_ERROR (Status)) {
return Status;
}
}
}
}
}
}
return EFI_SUCCESS;
}
/**
Dump the PCI device information managed by this VTd engine.
@param[in] VtdIndex The index of VTd engine.
**/
VOID
DumpPciDeviceInfo (
IN UINTN VtdIndex
)
{
UINTN Index;
DEBUG ((DEBUG_INFO,"PCI Device Information (Number 0x%x, IncludeAll - %d):\n",
mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceDataNumber,
mVtdUnitInformation[VtdIndex].PciDeviceInfo.IncludeAllFlag
));
for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceDataNumber; Index++) {
DEBUG ((DEBUG_INFO," S%04x B%02x D%02x F%02x\n",
mVtdUnitInformation[VtdIndex].Segment,
mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[Index].PciSourceId.Bits.Bus,
mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[Index].PciSourceId.Bits.Device,
mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[Index].PciSourceId.Bits.Function
));
}
}
/**
Find the VTd index by the Segment and SourceId.
@param[in] Segment The segment of the source.
@param[in] SourceId The SourceId of the source.
@param[out] ExtContextEntry The ExtContextEntry of the source.
@param[out] ContextEntry The ContextEntry of the source.
@return The index of the VTd engine.
@retval (UINTN)-1 The VTd engine is not found.
**/
UINTN
FindVtdIndexByPciDevice (
IN UINT16 Segment,
IN VTD_SOURCE_ID SourceId,
OUT VTD_EXT_CONTEXT_ENTRY **ExtContextEntry,
OUT VTD_CONTEXT_ENTRY **ContextEntry
)
{
UINTN VtdIndex;
VTD_ROOT_ENTRY *RootEntry;
VTD_CONTEXT_ENTRY *ContextEntryTable;
VTD_CONTEXT_ENTRY *ThisContextEntry;
VTD_EXT_ROOT_ENTRY *ExtRootEntry;
VTD_EXT_CONTEXT_ENTRY *ExtContextEntryTable;
VTD_EXT_CONTEXT_ENTRY *ThisExtContextEntry;
UINTN PciDataIndex;
for (VtdIndex = 0; VtdIndex < mVtdUnitNumber; VtdIndex++) {
if (Segment != mVtdUnitInformation[VtdIndex].Segment) {
continue;
}
PciDataIndex = GetPciDataIndex (VtdIndex, Segment, SourceId);
if (PciDataIndex == (UINTN)-1) {
continue;
}
// DEBUG ((DEBUG_INFO,"FindVtdIndex(0x%x) for S%04x B%02x D%02x F%02x\n", VtdIndex, Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
if (mVtdUnitInformation[VtdIndex].ExtRootEntryTable != 0) {
ExtRootEntry = &mVtdUnitInformation[VtdIndex].ExtRootEntryTable[SourceId.Index.RootIndex];
ExtContextEntryTable = (VTD_EXT_CONTEXT_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(ExtRootEntry->Bits.LowerContextTablePointerLo, ExtRootEntry->Bits.LowerContextTablePointerHi) ;
ThisExtContextEntry = &ExtContextEntryTable[SourceId.Index.ContextIndex];
if (ThisExtContextEntry->Bits.AddressWidth == 0) {
continue;
}
*ExtContextEntry = ThisExtContextEntry;
*ContextEntry = NULL;
} else {
RootEntry = &mVtdUnitInformation[VtdIndex].RootEntryTable[SourceId.Index.RootIndex];
ContextEntryTable = (VTD_CONTEXT_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(RootEntry->Bits.ContextTablePointerLo, RootEntry->Bits.ContextTablePointerHi) ;
ThisContextEntry = &ContextEntryTable[SourceId.Index.ContextIndex];
if (ThisContextEntry->Bits.AddressWidth == 0) {
continue;
}
*ExtContextEntry = NULL;
*ContextEntry = ThisContextEntry;
}
return VtdIndex;
}
return (UINTN)-1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,157 @@
/** @file
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "DmaProtection.h"
/**
Create extended context entry.
@param[in] VtdIndex The index of the VTd engine.
@retval EFI_SUCCESS The extended context entry is created.
@retval EFI_OUT_OF_RESOURCE No enough resource to create extended context entry.
**/
EFI_STATUS
CreateExtContextEntry (
IN UINTN VtdIndex
)
{
UINTN Index;
VOID *Buffer;
UINTN RootPages;
UINTN ContextPages;
VTD_EXT_ROOT_ENTRY *ExtRootEntry;
VTD_EXT_CONTEXT_ENTRY *ExtContextEntryTable;
VTD_EXT_CONTEXT_ENTRY *ExtContextEntry;
VTD_SOURCE_ID *PciSourceId;
VTD_SOURCE_ID SourceId;
UINTN MaxBusNumber;
UINTN EntryTablePages;
MaxBusNumber = 0;
for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceDataNumber; Index++) {
PciSourceId = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[Index].PciSourceId;
if (PciSourceId->Bits.Bus > MaxBusNumber) {
MaxBusNumber = PciSourceId->Bits.Bus;
}
}
DEBUG ((DEBUG_INFO," MaxBusNumber - 0x%x\n", MaxBusNumber));
RootPages = EFI_SIZE_TO_PAGES (sizeof (VTD_EXT_ROOT_ENTRY) * VTD_ROOT_ENTRY_NUMBER);
ContextPages = EFI_SIZE_TO_PAGES (sizeof (VTD_EXT_CONTEXT_ENTRY) * VTD_CONTEXT_ENTRY_NUMBER);
EntryTablePages = RootPages + ContextPages * (MaxBusNumber + 1);
Buffer = AllocateZeroPages (EntryTablePages);
if (Buffer == NULL) {
DEBUG ((DEBUG_INFO,"Could not Alloc Root Entry Table.. \n"));
return EFI_OUT_OF_RESOURCES;
}
mVtdUnitInformation[VtdIndex].ExtRootEntryTable = (VTD_EXT_ROOT_ENTRY *)Buffer;
Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (RootPages);
for (Index = 0; Index < mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceDataNumber; Index++) {
PciSourceId = &mVtdUnitInformation[VtdIndex].PciDeviceInfo.PciDeviceData[Index].PciSourceId;
SourceId.Bits.Bus = PciSourceId->Bits.Bus;
SourceId.Bits.Device = PciSourceId->Bits.Device;
SourceId.Bits.Function = PciSourceId->Bits.Function;
ExtRootEntry = &mVtdUnitInformation[VtdIndex].ExtRootEntryTable[SourceId.Index.RootIndex];
if (ExtRootEntry->Bits.LowerPresent == 0) {
ExtRootEntry->Bits.LowerContextTablePointerLo = (UINT32) RShiftU64 ((UINT64)(UINTN)Buffer, 12);
ExtRootEntry->Bits.LowerContextTablePointerHi = (UINT32) RShiftU64 ((UINT64)(UINTN)Buffer, 32);
ExtRootEntry->Bits.LowerPresent = 1;
ExtRootEntry->Bits.UpperContextTablePointerLo = (UINT32) RShiftU64 ((UINT64)(UINTN)Buffer, 12) + 1;
ExtRootEntry->Bits.UpperContextTablePointerHi = (UINT32) RShiftU64 (RShiftU64 ((UINT64)(UINTN)Buffer, 12) + 1, 20);
ExtRootEntry->Bits.UpperPresent = 1;
FlushPageTableMemory (VtdIndex, (UINTN)ExtRootEntry, sizeof(*ExtRootEntry));
Buffer = (UINT8 *)Buffer + EFI_PAGES_TO_SIZE (ContextPages);
}
ExtContextEntryTable = (VTD_EXT_CONTEXT_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(ExtRootEntry->Bits.LowerContextTablePointerLo, ExtRootEntry->Bits.LowerContextTablePointerHi) ;
ExtContextEntry = &ExtContextEntryTable[SourceId.Index.ContextIndex];
ExtContextEntry->Bits.TranslationType = 0;
ExtContextEntry->Bits.FaultProcessingDisable = 0;
ExtContextEntry->Bits.Present = 0;
DEBUG ((DEBUG_INFO,"DOMAIN: S%04x, B%02x D%02x F%02x\n", mVtdUnitInformation[VtdIndex].Segment, SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
switch (mVtdUnitInformation[VtdIndex].CapReg.Bits.SAGAW) {
case BIT1:
ExtContextEntry->Bits.AddressWidth = 0x1;
break;
case BIT2:
ExtContextEntry->Bits.AddressWidth = 0x2;
break;
}
FlushPageTableMemory (VtdIndex, (UINTN)ExtContextEntry, sizeof(*ExtContextEntry));
}
return EFI_SUCCESS;
}
/**
Dump DMAR extended context entry table.
@param[in] ExtRootEntry DMAR extended root entry.
**/
VOID
DumpDmarExtContextEntryTable (
IN VTD_EXT_ROOT_ENTRY *ExtRootEntry
)
{
UINTN Index;
UINTN Index2;
VTD_EXT_CONTEXT_ENTRY *ExtContextEntry;
DEBUG ((DEBUG_INFO,"=========================\n"));
DEBUG ((DEBUG_INFO,"DMAR ExtContext Entry Table:\n"));
DEBUG ((DEBUG_INFO,"ExtRootEntry Address - 0x%x\n", ExtRootEntry));
for (Index = 0; Index < VTD_ROOT_ENTRY_NUMBER; Index++) {
if ((ExtRootEntry[Index].Uint128.Uint64Lo != 0) || (ExtRootEntry[Index].Uint128.Uint64Hi != 0)) {
DEBUG ((DEBUG_INFO," ExtRootEntry(0x%02x) B%02x - 0x%016lx %016lx\n",
Index, Index, ExtRootEntry[Index].Uint128.Uint64Hi, ExtRootEntry[Index].Uint128.Uint64Lo));
}
if (ExtRootEntry[Index].Bits.LowerPresent == 0) {
continue;
}
ExtContextEntry = (VTD_EXT_CONTEXT_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(ExtRootEntry[Index].Bits.LowerContextTablePointerLo, ExtRootEntry[Index].Bits.LowerContextTablePointerHi);
for (Index2 = 0; Index2 < VTD_CONTEXT_ENTRY_NUMBER/2; Index2++) {
if ((ExtContextEntry[Index2].Uint256.Uint64_1 != 0) || (ExtContextEntry[Index2].Uint256.Uint64_2 != 0) ||
(ExtContextEntry[Index2].Uint256.Uint64_3 != 0) || (ExtContextEntry[Index2].Uint256.Uint64_4 != 0)) {
DEBUG ((DEBUG_INFO," ExtContextEntryLower(0x%02x) D%02xF%02x - 0x%016lx %016lx %016lx %016lx\n",
Index2, Index2 >> 3, Index2 & 0x7, ExtContextEntry[Index2].Uint256.Uint64_4, ExtContextEntry[Index2].Uint256.Uint64_3, ExtContextEntry[Index2].Uint256.Uint64_2, ExtContextEntry[Index2].Uint256.Uint64_1));
}
if (ExtContextEntry[Index2].Bits.Present == 0) {
continue;
}
}
if (ExtRootEntry[Index].Bits.UpperPresent == 0) {
continue;
}
ExtContextEntry = (VTD_EXT_CONTEXT_ENTRY *)(UINTN)VTD_64BITS_ADDRESS(ExtRootEntry[Index].Bits.UpperContextTablePointerLo, ExtRootEntry[Index].Bits.UpperContextTablePointerHi);
for (Index2 = 0; Index2 < VTD_CONTEXT_ENTRY_NUMBER/2; Index2++) {
if ((ExtContextEntry[Index2].Uint256.Uint64_1 != 0) || (ExtContextEntry[Index2].Uint256.Uint64_2 != 0) ||
(ExtContextEntry[Index2].Uint256.Uint64_3 != 0) || (ExtContextEntry[Index2].Uint256.Uint64_4 != 0)) {
DEBUG ((DEBUG_INFO," ExtContextEntryUpper(0x%02x) D%02xF%02x - 0x%016lx %016lx %016lx %016lx\n",
Index2, (Index2 + 128) >> 3, (Index2 + 128) & 0x7, ExtContextEntry[Index2].Uint256.Uint64_4, ExtContextEntry[Index2].Uint256.Uint64_3, ExtContextEntry[Index2].Uint256.Uint64_2, ExtContextEntry[Index2].Uint256.Uint64_1));
}
if (ExtContextEntry[Index2].Bits.Present == 0) {
continue;
}
}
}
DEBUG ((DEBUG_INFO,"=========================\n"));
}

View File

@@ -0,0 +1,565 @@
/** @file
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "DmaProtection.h"
UINT64 mVtdHostAddressWidthMask;
UINTN mVtdUnitNumber;
VTD_UNIT_INFORMATION *mVtdUnitInformation;
BOOLEAN mVtdEnabled;
/**
Flush VTD page table and context table memory.
This action is to make sure the IOMMU engine can get final data in memory.
@param[in] VtdIndex The index used to identify a VTd engine.
@param[in] Base The base address of memory to be flushed.
@param[in] Size The size of memory in bytes to be flushed.
**/
VOID
FlushPageTableMemory (
IN UINTN VtdIndex,
IN UINTN Base,
IN UINTN Size
)
{
if (mVtdUnitInformation[VtdIndex].ECapReg.Bits.C == 0) {
WriteBackDataCacheRange ((VOID *)Base, Size);
}
}
/**
Flush VTd engine write buffer.
@param[in] VtdIndex The index used to identify a VTd engine.
**/
VOID
FlushWriteBuffer (
IN UINTN VtdIndex
)
{
UINT32 Reg32;
if (mVtdUnitInformation[VtdIndex].CapReg.Bits.RWBF != 0) {
Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GSTS_REG);
MmioWrite32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GCMD_REG, Reg32 | B_GMCD_REG_WBF);
do {
Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GSTS_REG);
} while ((Reg32 & B_GSTS_REG_WBF) != 0);
}
}
/**
Invalidate VTd context cache.
@param[in] VtdIndex The index used to identify a VTd engine.
**/
EFI_STATUS
InvalidateContextCache (
IN UINTN VtdIndex
)
{
UINT64 Reg64;
Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG);
if ((Reg64 & B_CCMD_REG_ICC) != 0) {
DEBUG ((DEBUG_ERROR,"ERROR: InvalidateContextCache: B_CCMD_REG_ICC is set for VTD(%d)\n",VtdIndex));
return EFI_DEVICE_ERROR;
}
Reg64 &= ((~B_CCMD_REG_ICC) & (~B_CCMD_REG_CIRG_MASK));
Reg64 |= (B_CCMD_REG_ICC | V_CCMD_REG_CIRG_GLOBAL);
MmioWrite64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG, Reg64);
do {
Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG);
} while ((Reg64 & B_CCMD_REG_ICC) != 0);
return EFI_SUCCESS;
}
/**
Invalidate VTd IOTLB.
@param[in] VtdIndex The index used to identify a VTd engine.
**/
EFI_STATUS
InvalidateIOTLB (
IN UINTN VtdIndex
)
{
UINT64 Reg64;
Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);
if ((Reg64 & B_IOTLB_REG_IVT) != 0) {
DEBUG ((DEBUG_ERROR,"ERROR: InvalidateIOTLB: B_IOTLB_REG_IVT is set for VTD(%d)\n", VtdIndex));
return EFI_DEVICE_ERROR;
}
Reg64 &= ((~B_IOTLB_REG_IVT) & (~B_IOTLB_REG_IIRG_MASK));
Reg64 |= (B_IOTLB_REG_IVT | V_IOTLB_REG_IIRG_GLOBAL);
MmioWrite64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG, Reg64);
do {
Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);
} while ((Reg64 & B_IOTLB_REG_IVT) != 0);
return EFI_SUCCESS;
}
/**
Invalid VTd global IOTLB.
@param[in] VtdIndex The index of VTd engine.
@retval EFI_SUCCESS VTd global IOTLB is invalidated.
@retval EFI_DEVICE_ERROR VTd global IOTLB is not invalidated.
**/
EFI_STATUS
InvalidateVtdIOTLBGlobal (
IN UINTN VtdIndex
)
{
if (!mVtdEnabled) {
return EFI_SUCCESS;
}
DEBUG((DEBUG_VERBOSE, "InvalidateVtdIOTLBGlobal(%d)\n", VtdIndex));
//
// Write Buffer Flush before invalidation
//
FlushWriteBuffer (VtdIndex);
//
// Invalidate the context cache
//
if (mVtdUnitInformation[VtdIndex].HasDirtyContext) {
InvalidateContextCache (VtdIndex);
}
//
// Invalidate the IOTLB cache
//
if (mVtdUnitInformation[VtdIndex].HasDirtyContext || mVtdUnitInformation[VtdIndex].HasDirtyPages) {
InvalidateIOTLB (VtdIndex);
}
return EFI_SUCCESS;
}
/**
Prepare VTD configuration.
**/
VOID
PrepareVtdConfig (
VOID
)
{
UINTN Index;
UINTN DomainNumber;
for (Index = 0; Index < mVtdUnitNumber; Index++) {
DEBUG ((DEBUG_INFO, "Dump VTd Capability (%d)\n", Index));
mVtdUnitInformation[Index].CapReg.Uint64 = MmioRead64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_CAP_REG);
DumpVtdCapRegs (&mVtdUnitInformation[Index].CapReg);
mVtdUnitInformation[Index].ECapReg.Uint64 = MmioRead64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_ECAP_REG);
DumpVtdECapRegs (&mVtdUnitInformation[Index].ECapReg);
if ((mVtdUnitInformation[Index].CapReg.Bits.SLLPS & BIT0) == 0) {
DEBUG((DEBUG_WARN, "!!!! 2MB super page is not supported on VTD %d !!!!\n", Index));
}
if ((mVtdUnitInformation[Index].CapReg.Bits.SAGAW & BIT2) == 0) {
DEBUG((DEBUG_ERROR, "!!!! 4-level page-table is not supported on VTD %d !!!!\n", Index));
return ;
}
DomainNumber = (UINTN)1 << (UINT8)((UINTN)mVtdUnitInformation[Index].CapReg.Bits.ND * 2 + 4);
if (mVtdUnitInformation[Index].PciDeviceInfo.PciDeviceDataNumber >= DomainNumber) {
DEBUG((DEBUG_ERROR, "!!!! Pci device Number(0x%x) >= DomainNumber(0x%x) !!!!\n", mVtdUnitInformation[Index].PciDeviceInfo.PciDeviceDataNumber, DomainNumber));
return ;
}
}
return ;
}
/**
Disable PMR in all VTd engine.
**/
VOID
DisablePmr (
VOID
)
{
UINT32 Reg32;
VTD_CAP_REG CapReg;
UINTN Index;
DEBUG ((DEBUG_INFO,"DisablePmr\n"));
for (Index = 0; Index < mVtdUnitNumber; Index++) {
CapReg.Uint64 = MmioRead64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_CAP_REG);
if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {
continue ;
}
Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
if ((Reg32 & BIT0) != 0) {
MmioWrite32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_PMEN_ENABLE_REG, 0x0);
do {
Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
} while((Reg32 & BIT0) != 0);
DEBUG ((DEBUG_INFO,"Pmr(%d) disabled\n", Index));
} else {
DEBUG ((DEBUG_INFO,"Pmr(%d) not enabled\n", Index));
}
}
return ;
}
/**
Enable DMAR translation.
@retval EFI_SUCCESS DMAR translation is enabled.
@retval EFI_DEVICE_ERROR DMAR translation is not enabled.
**/
EFI_STATUS
EnableDmar (
VOID
)
{
UINTN Index;
UINT32 Reg32;
for (Index = 0; Index < mVtdUnitNumber; Index++) {
DEBUG((DEBUG_INFO, ">>>>>>EnableDmar() for engine [%d] \n", Index));
if (mVtdUnitInformation[Index].ExtRootEntryTable != NULL) {
DEBUG((DEBUG_INFO, "ExtRootEntryTable 0x%x \n", mVtdUnitInformation[Index].ExtRootEntryTable));
MmioWrite64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_RTADDR_REG, (UINT64)(UINTN)mVtdUnitInformation[Index].ExtRootEntryTable | BIT11);
} else {
DEBUG((DEBUG_INFO, "RootEntryTable 0x%x \n", mVtdUnitInformation[Index].RootEntryTable));
MmioWrite64 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_RTADDR_REG, (UINT64)(UINTN)mVtdUnitInformation[Index].RootEntryTable);
}
MmioWrite32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_SRTP);
DEBUG((DEBUG_INFO, "EnableDmar: waiting for RTPS bit to be set... \n"));
do {
Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GSTS_REG);
} while((Reg32 & B_GSTS_REG_RTPS) == 0);
//
// Init DMAr Fault Event and Data registers
//
Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_FEDATA_REG);
//
// Write Buffer Flush before invalidation
//
FlushWriteBuffer (Index);
//
// Invalidate the context cache
//
InvalidateContextCache (Index);
//
// Invalidate the IOTLB cache
//
InvalidateIOTLB (Index);
//
// Enable VTd
//
MmioWrite32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_TE);
DEBUG((DEBUG_INFO, "EnableDmar: Waiting B_GSTS_REG_TE ...\n"));
do {
Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GSTS_REG);
} while ((Reg32 & B_GSTS_REG_TE) == 0);
DEBUG ((DEBUG_INFO,"VTD (%d) enabled!<<<<<<\n",Index));
}
//
// Need disable PMR, since we already setup translation table.
//
DisablePmr ();
mVtdEnabled = TRUE;
return EFI_SUCCESS;
}
/**
Disable DMAR translation.
@retval EFI_SUCCESS DMAR translation is disabled.
@retval EFI_DEVICE_ERROR DMAR translation is not disabled.
**/
EFI_STATUS
DisableDmar (
VOID
)
{
UINTN Index;
UINTN SubIndex;
UINT32 Reg32;
for (Index = 0; Index < mVtdUnitNumber; Index++) {
DEBUG((DEBUG_INFO, ">>>>>>DisableDmar() for engine [%d] \n", Index));
//
// Write Buffer Flush before invalidation
//
FlushWriteBuffer (Index);
//
// Disable VTd
//
MmioWrite32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_SRTP);
do {
Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GSTS_REG);
} while((Reg32 & B_GSTS_REG_RTPS) == 0);
Reg32 = MmioRead32 (mVtdUnitInformation[Index].VtdUnitBaseAddress + R_GSTS_REG);
DEBUG((DEBUG_INFO, "DisableDmar: GSTS_REG - 0x%08x\n", Reg32));
DEBUG ((DEBUG_INFO,"VTD (%d) Disabled!<<<<<<\n",Index));
}
mVtdEnabled = FALSE;
for (Index = 0; Index < mVtdUnitNumber; Index++) {
DEBUG((DEBUG_INFO, "engine [%d] access\n", Index));
for (SubIndex = 0; SubIndex < mVtdUnitInformation[Index].PciDeviceInfo.PciDeviceDataNumber; SubIndex++) {
DEBUG ((DEBUG_INFO, " PCI S%04X B%02x D%02x F%02x - %d\n",
mVtdUnitInformation[Index].Segment,
mVtdUnitInformation[Index].PciDeviceInfo.PciDeviceData[Index].PciSourceId.Bits.Bus,
mVtdUnitInformation[Index].PciDeviceInfo.PciDeviceData[Index].PciSourceId.Bits.Device,
mVtdUnitInformation[Index].PciDeviceInfo.PciDeviceData[Index].PciSourceId.Bits.Function,
mVtdUnitInformation[Index].PciDeviceInfo.PciDeviceData[Index].AccessCount
));
}
}
return EFI_SUCCESS;
}
/**
Dump VTd capability registers.
@param[in] CapReg The capability register.
**/
VOID
DumpVtdCapRegs (
IN VTD_CAP_REG *CapReg
)
{
DEBUG((DEBUG_INFO, " CapReg:\n", CapReg->Uint64));
DEBUG((DEBUG_INFO, " ND - 0x%x\n", CapReg->Bits.ND));
DEBUG((DEBUG_INFO, " AFL - 0x%x\n", CapReg->Bits.AFL));
DEBUG((DEBUG_INFO, " RWBF - 0x%x\n", CapReg->Bits.RWBF));
DEBUG((DEBUG_INFO, " PLMR - 0x%x\n", CapReg->Bits.PLMR));
DEBUG((DEBUG_INFO, " PHMR - 0x%x\n", CapReg->Bits.PHMR));
DEBUG((DEBUG_INFO, " CM - 0x%x\n", CapReg->Bits.CM));
DEBUG((DEBUG_INFO, " SAGAW - 0x%x\n", CapReg->Bits.SAGAW));
DEBUG((DEBUG_INFO, " MGAW - 0x%x\n", CapReg->Bits.MGAW));
DEBUG((DEBUG_INFO, " ZLR - 0x%x\n", CapReg->Bits.ZLR));
DEBUG((DEBUG_INFO, " FRO - 0x%x\n", CapReg->Bits.FRO));
DEBUG((DEBUG_INFO, " SLLPS - 0x%x\n", CapReg->Bits.SLLPS));
DEBUG((DEBUG_INFO, " PSI - 0x%x\n", CapReg->Bits.PSI));
DEBUG((DEBUG_INFO, " NFR - 0x%x\n", CapReg->Bits.NFR));
DEBUG((DEBUG_INFO, " MAMV - 0x%x\n", CapReg->Bits.MAMV));
DEBUG((DEBUG_INFO, " DWD - 0x%x\n", CapReg->Bits.DWD));
DEBUG((DEBUG_INFO, " DRD - 0x%x\n", CapReg->Bits.DRD));
DEBUG((DEBUG_INFO, " FL1GP - 0x%x\n", CapReg->Bits.FL1GP));
DEBUG((DEBUG_INFO, " PI - 0x%x\n", CapReg->Bits.PI));
}
/**
Dump VTd extended capability registers.
@param[in] ECapReg The extended capability register.
**/
VOID
DumpVtdECapRegs (
IN VTD_ECAP_REG *ECapReg
)
{
DEBUG((DEBUG_INFO, " ECapReg:\n", ECapReg->Uint64));
DEBUG((DEBUG_INFO, " C - 0x%x\n", ECapReg->Bits.C));
DEBUG((DEBUG_INFO, " QI - 0x%x\n", ECapReg->Bits.QI));
DEBUG((DEBUG_INFO, " DT - 0x%x\n", ECapReg->Bits.DT));
DEBUG((DEBUG_INFO, " IR - 0x%x\n", ECapReg->Bits.IR));
DEBUG((DEBUG_INFO, " EIM - 0x%x\n", ECapReg->Bits.EIM));
DEBUG((DEBUG_INFO, " PT - 0x%x\n", ECapReg->Bits.PT));
DEBUG((DEBUG_INFO, " SC - 0x%x\n", ECapReg->Bits.SC));
DEBUG((DEBUG_INFO, " IRO - 0x%x\n", ECapReg->Bits.IRO));
DEBUG((DEBUG_INFO, " MHMV - 0x%x\n", ECapReg->Bits.MHMV));
DEBUG((DEBUG_INFO, " ECS - 0x%x\n", ECapReg->Bits.ECS));
DEBUG((DEBUG_INFO, " MTS - 0x%x\n", ECapReg->Bits.MTS));
DEBUG((DEBUG_INFO, " NEST - 0x%x\n", ECapReg->Bits.NEST));
DEBUG((DEBUG_INFO, " DIS - 0x%x\n", ECapReg->Bits.DIS));
DEBUG((DEBUG_INFO, " PASID - 0x%x\n", ECapReg->Bits.PASID));
DEBUG((DEBUG_INFO, " PRS - 0x%x\n", ECapReg->Bits.PRS));
DEBUG((DEBUG_INFO, " ERS - 0x%x\n", ECapReg->Bits.ERS));
DEBUG((DEBUG_INFO, " SRS - 0x%x\n", ECapReg->Bits.SRS));
DEBUG((DEBUG_INFO, " NWFS - 0x%x\n", ECapReg->Bits.NWFS));
DEBUG((DEBUG_INFO, " EAFS - 0x%x\n", ECapReg->Bits.EAFS));
DEBUG((DEBUG_INFO, " PSS - 0x%x\n", ECapReg->Bits.PSS));
}
/**
Dump VTd registers.
@param[in] VtdIndex The index of VTd engine.
**/
VOID
DumpVtdRegs (
IN UINTN VtdIndex
)
{
UINTN Index;
UINT64 Reg64;
VTD_FRCD_REG FrcdReg;
VTD_CAP_REG CapReg;
UINT32 Reg32;
VTD_SOURCE_ID SourceId;
DEBUG((DEBUG_INFO, "#### DumpVtdRegs(%d) Begin ####\n", VtdIndex));
Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_VER_REG);
DEBUG((DEBUG_INFO, " VER_REG - 0x%08x\n", Reg32));
CapReg.Uint64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CAP_REG);
DEBUG((DEBUG_INFO, " CAP_REG - 0x%016lx\n", CapReg.Uint64));
Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_ECAP_REG);
DEBUG((DEBUG_INFO, " ECAP_REG - 0x%016lx\n", Reg64));
Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_GSTS_REG);
DEBUG((DEBUG_INFO, " GSTS_REG - 0x%08x \n", Reg32));
Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_RTADDR_REG);
DEBUG((DEBUG_INFO, " RTADDR_REG - 0x%016lx\n", Reg64));
Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_CCMD_REG);
DEBUG((DEBUG_INFO, " CCMD_REG - 0x%016lx\n", Reg64));
Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FSTS_REG);
DEBUG((DEBUG_INFO, " FSTS_REG - 0x%08x\n", Reg32));
Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FECTL_REG);
DEBUG((DEBUG_INFO, " FECTL_REG - 0x%08x\n", Reg32));
Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FEDATA_REG);
DEBUG((DEBUG_INFO, " FEDATA_REG - 0x%08x\n", Reg32));
Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FEADDR_REG);
DEBUG((DEBUG_INFO, " FEADDR_REG - 0x%08x\n",Reg32));
Reg32 = MmioRead32 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + R_FEUADDR_REG);
DEBUG((DEBUG_INFO, " FEUADDR_REG - 0x%08x\n",Reg32));
for (Index = 0; Index < (UINTN)CapReg.Bits.NFR + 1; Index++) {
FrcdReg.Uint64[0] = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG));
FrcdReg.Uint64[1] = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG + sizeof(UINT64)));
DEBUG((DEBUG_INFO, " FRCD_REG[%d] - 0x%016lx %016lx\n", Index, FrcdReg.Uint64[1], FrcdReg.Uint64[0]));
if (FrcdReg.Uint64[1] != 0 || FrcdReg.Uint64[0] != 0) {
DEBUG((DEBUG_INFO, " Fault Info - 0x%016lx\n", VTD_64BITS_ADDRESS(FrcdReg.Bits.FILo, FrcdReg.Bits.FIHi)));
SourceId.Uint16 = (UINT16)FrcdReg.Bits.SID;
DEBUG((DEBUG_INFO, " Source - B%02x D%02x F%02x\n", SourceId.Bits.Bus, SourceId.Bits.Device, SourceId.Bits.Function));
DEBUG((DEBUG_INFO, " Type - %x (%a)\n", FrcdReg.Bits.T, FrcdReg.Bits.T ? "read" : "write"));
DEBUG((DEBUG_INFO, " Reason - %x\n", FrcdReg.Bits.FR));
}
}
Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IVA_REG);
DEBUG((DEBUG_INFO, " IVA_REG - 0x%016lx\n",Reg64));
Reg64 = MmioRead64 (mVtdUnitInformation[VtdIndex].VtdUnitBaseAddress + (mVtdUnitInformation[VtdIndex].ECapReg.Bits.IRO * 16) + R_IOTLB_REG);
DEBUG((DEBUG_INFO, " IOTLB_REG - 0x%016lx\n",Reg64));
DEBUG((DEBUG_INFO, "#### DumpVtdRegs(%d) End ####\n", VtdIndex));
}
/**
Dump VTd registers for all VTd engine.
**/
VOID
DumpVtdRegsAll (
VOID
)
{
UINTN Num;
for (Num = 0; Num < mVtdUnitNumber; Num++) {
DumpVtdRegs (Num);
}
}
/**
Dump VTd registers if there is error.
**/
VOID
DumpVtdIfError (
VOID
)
{
UINTN Num;
UINTN Index;
VTD_FRCD_REG FrcdReg;
VTD_CAP_REG CapReg;
UINT32 Reg32;
BOOLEAN HasError;
for (Num = 0; Num < mVtdUnitNumber; Num++) {
HasError = FALSE;
Reg32 = MmioRead32 (mVtdUnitInformation[Num].VtdUnitBaseAddress + R_FSTS_REG);
if (Reg32 != 0) {
HasError = TRUE;
}
Reg32 = MmioRead32 (mVtdUnitInformation[Num].VtdUnitBaseAddress + R_FECTL_REG);
if ((Reg32 & BIT30) != 0) {
HasError = TRUE;
}
CapReg.Uint64 = MmioRead64 (mVtdUnitInformation[Num].VtdUnitBaseAddress + R_CAP_REG);
for (Index = 0; Index < (UINTN)CapReg.Bits.NFR + 1; Index++) {
FrcdReg.Uint64[0] = MmioRead64 (mVtdUnitInformation[Num].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG));
FrcdReg.Uint64[1] = MmioRead64 (mVtdUnitInformation[Num].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG + sizeof(UINT64)));
if (FrcdReg.Bits.F != 0) {
HasError = TRUE;
}
}
if (HasError) {
DEBUG((DEBUG_INFO, "\n#### ERROR ####\n"));
DumpVtdRegs (Num);
DEBUG((DEBUG_INFO, "#### ERROR ####\n\n"));
//
// Clear
//
for (Index = 0; Index < (UINTN)CapReg.Bits.NFR + 1; Index++) {
FrcdReg.Uint64[1] = MmioRead64 (mVtdUnitInformation[Num].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG + sizeof(UINT64)));
if (FrcdReg.Bits.F != 0) {
FrcdReg.Bits.F = 0;
MmioWrite64 (mVtdUnitInformation[Num].VtdUnitBaseAddress + ((CapReg.Bits.FRO * 16) + (Index * 16) + R_FRCD_REG + sizeof(UINT64)), FrcdReg.Uint64[1]);
}
MmioWrite32 (mVtdUnitInformation[Num].VtdUnitBaseAddress + R_FSTS_REG, MmioRead32 (mVtdUnitInformation[Num].VtdUnitBaseAddress + R_FSTS_REG));
}
}
}
}