]> git.proxmox.com Git - mirror_edk2.git/blobdiff - IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c
IntelSiliconPkg/IntelVTdDxe: Move to feature dir.
[mirror_edk2.git] / IntelSiliconPkg / Feature / VTd / IntelVTdDxe / DmaProtection.c
diff --git a/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c b/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c
new file mode 100644 (file)
index 0000000..f5de01f
--- /dev/null
@@ -0,0 +1,503 @@
+/** @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