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