| /** @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 ; | |
| } |