1. Do not use tab characters 2. No trailing white space in one line 3. All files must end with CRLF Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Liming Gao <liming.gao@intel.com>
		
			
				
	
	
		
			673 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			673 lines
		
	
	
		
			21 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 ();
 | |
| 
 | |
|   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 ;
 | |
| }
 |