X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=IntelSiliconPkg%2FFeature%2FVTd%2FIntelVTdDxe%2FDmaProtection.c;fp=IntelSiliconPkg%2FFeature%2FVTd%2FIntelVTdDxe%2FDmaProtection.c;h=f5de01f3806cc35a87cac2311049c77b00272a75;hp=0000000000000000000000000000000000000000;hb=9010459c9aaa985619e341ecc2789a06993b2310;hpb=b575ca32c8b05af5c23f46728ccf4937f2889ba8 diff --git a/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c b/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c new file mode 100644 index 0000000000..f5de01f380 --- /dev/null +++ b/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c @@ -0,0 +1,503 @@ +/** @file + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ 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" + +EFI_ACPI_SDT_PROTOCOL *mAcpiSdt; +UINT64 mBelow4GMemoryLimit; +UINT64 mAbove4GMemoryLimit; + +EDKII_PLATFORM_VTD_POLICY_PROTOCOL *mPlatformVTdPolicy; + +/** + 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, "GetDmarAcpiTable\n")); + Status = GetDmarAcpiTable (); + if (EFI_ERROR (Status)) { + return; + } + 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 (); + + 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 (); +} + +/** + ACPI notification function. + + @param[in] Table A pointer to the ACPI table header. + @param[in] Version The ACPI table's version. + @param[in] TableKey The table key for this ACPI table. + + @retval EFI_SUCCESS The notification function is executed. +**/ +EFI_STATUS +EFIAPI +AcpiNotificationFunc ( + IN EFI_ACPI_SDT_HEADER *Table, + IN EFI_ACPI_TABLE_VERSION Version, + IN UINTN TableKey + ) +{ + if (Table->Signature == EFI_ACPI_4_0_DMA_REMAPPING_TABLE_SIGNATURE) { + DEBUG((DEBUG_INFO, "Vtd AcpiNotificationFunc\n")); + SetupVtd (); + } + return EFI_SUCCESS; +} + +/** + 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; + + Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (VOID **) &mAcpiSdt); + ASSERT_EFI_ERROR (Status); + + Status = mAcpiSdt->RegisterNotify (TRUE, AcpiNotificationFunc); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + OnExitBootServices, + NULL, + &gEfiEventExitBootServicesGuid, + &ExitBootServicesEvent + ); + ASSERT_EFI_ERROR (Status); + + Status = EfiCreateEventLegacyBootEx ( + TPL_NOTIFY, + OnLegacyBoot, + NULL, + &LegacyBootEvent + ); + ASSERT_EFI_ERROR (Status); + + return ; +}