+++ /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