REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1272 To have high confidence in usage for platform, add option (BIT2 of PcdVTdPolicyPropertyMask) to force no IOMMU access attribute request recording before DMAR table is installed. Check PcdVTdPolicyPropertyMask BIT2 before RequestAccessAttribute() and ProcessRequestedAccessAttribute(), then RequestAccessAttribute(), ProcessRequestedAccessAttribute() and mAccessRequestXXX variables could be optimized by compiler when PcdVTdPolicyPropertyMask BIT2 = 1. Test done: 1: Created case that has IOMMU access attribute request before DMAR table is installed, ASSERT was triggered after setting PcdVTdPolicyPropertyMask BIT2 to 1. 2. Confirmed RequestAccessAttribute(), ProcessRequestedAccessAttribute() and mAccessRequestXXX variables were optimized by compiler after setting PcdVTdPolicyPropertyMask BIT2 to 1. Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Rangasai V Chaganty <rangasai.v.chaganty@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Star Zeng <star.zeng@intel.com> Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
679 lines
22 KiB
C
679 lines
22 KiB
C
/** @file
|
|
|
|
Copyright (c) 2017 - 2018, 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 mBelow4GMemoryLimit;
|
|
UINT64 mAbove4GMemoryLimit;
|
|
|
|
EDKII_PLATFORM_VTD_POLICY_PROTOCOL *mPlatformVTdPolicy;
|
|
|
|
VTD_ACCESS_REQUEST *mAccessRequest = NULL;
|
|
UINTN mAccessRequestCount = 0;
|
|
UINTN mAccessRequestMaxCount = 0;
|
|
|
|
/**
|
|
Append VTd Access Request to global.
|
|
|
|
@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
|
|
RequestAccessAttribute (
|
|
IN UINT16 Segment,
|
|
IN VTD_SOURCE_ID SourceId,
|
|
IN UINT64 BaseAddress,
|
|
IN UINT64 Length,
|
|
IN UINT64 IoMmuAccess
|
|
)
|
|
{
|
|
VTD_ACCESS_REQUEST *NewAccessRequest;
|
|
UINTN Index;
|
|
|
|
//
|
|
// Optimization for memory.
|
|
//
|
|
// If the last record is to IoMmuAccess=0,
|
|
// Check previous records and remove the matched entry.
|
|
//
|
|
if (IoMmuAccess == 0) {
|
|
for (Index = 0; Index < mAccessRequestCount; Index++) {
|
|
if ((mAccessRequest[Index].Segment == Segment) &&
|
|
(mAccessRequest[Index].SourceId.Uint16 == SourceId.Uint16) &&
|
|
(mAccessRequest[Index].BaseAddress == BaseAddress) &&
|
|
(mAccessRequest[Index].Length == Length) &&
|
|
(mAccessRequest[Index].IoMmuAccess != 0)) {
|
|
//
|
|
// Remove this record [Index].
|
|
// No need to add the new record.
|
|
//
|
|
if (Index != mAccessRequestCount - 1) {
|
|
CopyMem (
|
|
&mAccessRequest[Index],
|
|
&mAccessRequest[Index + 1],
|
|
sizeof (VTD_ACCESS_REQUEST) * (mAccessRequestCount - 1 - Index)
|
|
);
|
|
}
|
|
ZeroMem (&mAccessRequest[mAccessRequestCount - 1], sizeof(VTD_ACCESS_REQUEST));
|
|
mAccessRequestCount--;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mAccessRequestCount >= mAccessRequestMaxCount) {
|
|
NewAccessRequest = AllocateZeroPool (sizeof(*NewAccessRequest) * (mAccessRequestMaxCount + MAX_VTD_ACCESS_REQUEST));
|
|
if (NewAccessRequest == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
mAccessRequestMaxCount += MAX_VTD_ACCESS_REQUEST;
|
|
if (mAccessRequest != NULL) {
|
|
CopyMem (NewAccessRequest, mAccessRequest, sizeof(*NewAccessRequest) * mAccessRequestCount);
|
|
FreePool (mAccessRequest);
|
|
}
|
|
mAccessRequest = NewAccessRequest;
|
|
}
|
|
|
|
ASSERT (mAccessRequestCount < mAccessRequestMaxCount);
|
|
|
|
mAccessRequest[mAccessRequestCount].Segment = Segment;
|
|
mAccessRequest[mAccessRequestCount].SourceId = SourceId;
|
|
mAccessRequest[mAccessRequestCount].BaseAddress = BaseAddress;
|
|
mAccessRequest[mAccessRequestCount].Length = Length;
|
|
mAccessRequest[mAccessRequestCount].IoMmuAccess = IoMmuAccess;
|
|
|
|
mAccessRequestCount++;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Process Access Requests from before DMAR table is installed.
|
|
|
|
**/
|
|
VOID
|
|
ProcessRequestedAccessAttribute (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
|
|
DEBUG ((DEBUG_INFO, "ProcessRequestedAccessAttribute ...\n"));
|
|
|
|
for (Index = 0; Index < mAccessRequestCount; Index++) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"PCI(S%x.B%x.D%x.F%x) ",
|
|
mAccessRequest[Index].Segment,
|
|
mAccessRequest[Index].SourceId.Bits.Bus,
|
|
mAccessRequest[Index].SourceId.Bits.Device,
|
|
mAccessRequest[Index].SourceId.Bits.Function
|
|
));
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"(0x%lx~0x%lx) - %lx\n",
|
|
mAccessRequest[Index].BaseAddress,
|
|
mAccessRequest[Index].Length,
|
|
mAccessRequest[Index].IoMmuAccess
|
|
));
|
|
Status = SetAccessAttribute (
|
|
mAccessRequest[Index].Segment,
|
|
mAccessRequest[Index].SourceId,
|
|
mAccessRequest[Index].BaseAddress,
|
|
mAccessRequest[Index].Length,
|
|
mAccessRequest[Index].IoMmuAccess
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "SetAccessAttribute %r: ", Status));
|
|
}
|
|
}
|
|
|
|
if (mAccessRequest != NULL) {
|
|
FreePool (mAccessRequest);
|
|
}
|
|
mAccessRequest = NULL;
|
|
mAccessRequestCount = 0;
|
|
mAccessRequestMaxCount = 0;
|
|
|
|
DEBUG ((DEBUG_INFO, "ProcessRequestedAccessAttribute Done\n"));
|
|
}
|
|
|
|
/**
|
|
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, "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 ();
|
|
|
|
if ((PcdGet8 (PcdVTdPolicyPropertyMask) & BIT2) == 0) {
|
|
//
|
|
// Support IOMMU access attribute request recording before DMAR table is installed.
|
|
// Here is to process the requests.
|
|
//
|
|
ProcessRequestedAccessAttribute ();
|
|
}
|
|
|
|
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 ();
|
|
}
|
|
|
|
/**
|
|
Notification function of ACPI Table change.
|
|
|
|
This is a notification function registered on ACPI Table change event.
|
|
|
|
@param Event Event whose notification function is being invoked.
|
|
@param Context Pointer to the notification function's context.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
AcpiNotificationFunc (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = GetDmarAcpiTable ();
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status == EFI_ALREADY_STARTED) {
|
|
gBS->CloseEvent (Event);
|
|
}
|
|
return;
|
|
}
|
|
SetupVtd ();
|
|
gBS->CloseEvent (Event);
|
|
}
|
|
|
|
/**
|
|
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;
|
|
EFI_EVENT EventAcpi10;
|
|
EFI_EVENT EventAcpi20;
|
|
|
|
Status = gBS->CreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
VTD_TPL_LEVEL,
|
|
AcpiNotificationFunc,
|
|
NULL,
|
|
&gEfiAcpi10TableGuid,
|
|
&EventAcpi10
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Status = gBS->CreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
VTD_TPL_LEVEL,
|
|
AcpiNotificationFunc,
|
|
NULL,
|
|
&gEfiAcpi20TableGuid,
|
|
&EventAcpi20
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Signal the events initially for the case
|
|
// that DMAR table has been installed.
|
|
//
|
|
gBS->SignalEvent (EventAcpi20);
|
|
gBS->SignalEvent (EventAcpi10);
|
|
|
|
Status = gBS->CreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_CALLBACK,
|
|
OnExitBootServices,
|
|
NULL,
|
|
&gEfiEventExitBootServicesGuid,
|
|
&ExitBootServicesEvent
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Status = EfiCreateEventLegacyBootEx (
|
|
TPL_CALLBACK,
|
|
OnLegacyBoot,
|
|
NULL,
|
|
&LegacyBootEvent
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return ;
|
|
}
|