/** @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 #include #include #include #include #include #include #include #include "IntelVTdPmrPei.h" /** Dump DMAR DeviceScopeEntry. @param[in] DmarDeviceScopeEntry DMAR DeviceScopeEntry **/ VOID DumpDmarDeviceScopeEntry ( IN EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *DmarDeviceScopeEntry ) { UINTN PciPathNumber; UINTN PciPathIndex; EFI_ACPI_DMAR_PCI_PATH *PciPath; if (DmarDeviceScopeEntry == NULL) { return; } DEBUG ((DEBUG_INFO, " *************************************************************************\n" )); DEBUG ((DEBUG_INFO, " * DMA-Remapping Device Scope Entry Structure *\n" )); DEBUG ((DEBUG_INFO, " *************************************************************************\n" )); DEBUG ((DEBUG_INFO, (sizeof(UINTN) == sizeof(UINT64)) ? " DMAR Device Scope Entry address ...................... 0x%016lx\n" : " DMAR Device Scope Entry address ...................... 0x%08x\n", DmarDeviceScopeEntry )); DEBUG ((DEBUG_INFO, " Device Scope Entry Type ............................ 0x%02x\n", DmarDeviceScopeEntry->Type )); switch (DmarDeviceScopeEntry->Type) { case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT: DEBUG ((DEBUG_INFO, " PCI Endpoint Device\n" )); break; case EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_BRIDGE: DEBUG ((DEBUG_INFO, " PCI Sub-hierachy\n" )); break; default: break; } DEBUG ((DEBUG_INFO, " Length ............................................. 0x%02x\n", DmarDeviceScopeEntry->Length )); DEBUG ((DEBUG_INFO, " Enumeration ID ..................................... 0x%02x\n", DmarDeviceScopeEntry->EnumerationId )); DEBUG ((DEBUG_INFO, " Starting Bus Number ................................ 0x%02x\n", DmarDeviceScopeEntry->StartBusNumber )); PciPathNumber = (DmarDeviceScopeEntry->Length - sizeof(EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER)) / sizeof(EFI_ACPI_DMAR_PCI_PATH); PciPath = (EFI_ACPI_DMAR_PCI_PATH *)(DmarDeviceScopeEntry + 1); for (PciPathIndex = 0; PciPathIndex < PciPathNumber; PciPathIndex++) { DEBUG ((DEBUG_INFO, " Device ............................................. 0x%02x\n", PciPath[PciPathIndex].Device )); DEBUG ((DEBUG_INFO, " Function ........................................... 0x%02x\n", PciPath[PciPathIndex].Function )); } DEBUG ((DEBUG_INFO, " *************************************************************************\n\n" )); return; } /** Dump DMAR RMRR table. @param[in] Rmrr DMAR RMRR table **/ VOID DumpDmarRmrr ( IN EFI_ACPI_DMAR_RMRR_HEADER *Rmrr ) { EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *DmarDeviceScopeEntry; INTN RmrrLen; if (Rmrr == NULL) { return; } DEBUG ((DEBUG_INFO, " ***************************************************************************\n" )); DEBUG ((DEBUG_INFO, " * Reserved Memory Region Reporting Structure *\n" )); DEBUG ((DEBUG_INFO, " ***************************************************************************\n" )); DEBUG ((DEBUG_INFO, (sizeof(UINTN) == sizeof(UINT64)) ? " RMRR address ........................................... 0x%016lx\n" : " RMRR address ........................................... 0x%08x\n", Rmrr )); DEBUG ((DEBUG_INFO, " Type ................................................. 0x%04x\n", Rmrr->Header.Type )); DEBUG ((DEBUG_INFO, " Length ............................................... 0x%04x\n", Rmrr->Header.Length )); DEBUG ((DEBUG_INFO, " Segment Number ....................................... 0x%04x\n", Rmrr->SegmentNumber )); DEBUG ((DEBUG_INFO, " Reserved Memory Region Base Address .................. 0x%016lx\n", Rmrr->ReservedMemoryRegionBaseAddress )); DEBUG ((DEBUG_INFO, " Reserved Memory Region Limit Address ................. 0x%016lx\n", Rmrr->ReservedMemoryRegionLimitAddress )); RmrrLen = Rmrr->Header.Length - sizeof(EFI_ACPI_DMAR_RMRR_HEADER); DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)(Rmrr + 1); while (RmrrLen > 0) { DumpDmarDeviceScopeEntry (DmarDeviceScopeEntry); RmrrLen -= DmarDeviceScopeEntry->Length; DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)DmarDeviceScopeEntry + DmarDeviceScopeEntry->Length); } DEBUG ((DEBUG_INFO, " ***************************************************************************\n\n" )); return; } /** Dump DMAR DRHD table. @param[in] Drhd DMAR DRHD table **/ VOID DumpDmarDrhd ( IN EFI_ACPI_DMAR_DRHD_HEADER *Drhd ) { EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *DmarDeviceScopeEntry; INTN DrhdLen; if (Drhd == NULL) { return; } DEBUG ((DEBUG_INFO, " ***************************************************************************\n" )); DEBUG ((DEBUG_INFO, " * DMA-Remapping Hardware Definition Structure *\n" )); DEBUG ((DEBUG_INFO, " ***************************************************************************\n" )); DEBUG ((DEBUG_INFO, (sizeof(UINTN) == sizeof(UINT64)) ? " DRHD address ........................................... 0x%016lx\n" : " DRHD address ........................................... 0x%08x\n", Drhd )); DEBUG ((DEBUG_INFO, " Type ................................................. 0x%04x\n", Drhd->Header.Type )); DEBUG ((DEBUG_INFO, " Length ............................................... 0x%04x\n", Drhd->Header.Length )); DEBUG ((DEBUG_INFO, " Flags ................................................ 0x%02x\n", Drhd->Flags )); DEBUG ((DEBUG_INFO, " INCLUDE_PCI_ALL .................................... 0x%02x\n", Drhd->Flags & EFI_ACPI_DMAR_DRHD_FLAGS_INCLUDE_PCI_ALL )); DEBUG ((DEBUG_INFO, " Segment Number ....................................... 0x%04x\n", Drhd->SegmentNumber )); DEBUG ((DEBUG_INFO, " Register Base Address ................................ 0x%016lx\n", Drhd->RegisterBaseAddress )); DrhdLen = Drhd->Header.Length - sizeof(EFI_ACPI_DMAR_DRHD_HEADER); DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)(Drhd + 1); while (DrhdLen > 0) { DumpDmarDeviceScopeEntry (DmarDeviceScopeEntry); DrhdLen -= DmarDeviceScopeEntry->Length; DmarDeviceScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)DmarDeviceScopeEntry + DmarDeviceScopeEntry->Length); } DEBUG ((DEBUG_INFO, " ***************************************************************************\n\n" )); return; } /** Dump DMAR ACPI table. @param[in] Dmar DMAR ACPI table **/ VOID DumpAcpiDMAR ( IN EFI_ACPI_DMAR_HEADER *Dmar ) { EFI_ACPI_DMAR_STRUCTURE_HEADER *DmarHeader; INTN DmarLen; if (Dmar == NULL) { return; } // // Dump Dmar table // DEBUG ((DEBUG_INFO, "*****************************************************************************\n" )); DEBUG ((DEBUG_INFO, "* DMAR Table *\n" )); DEBUG ((DEBUG_INFO, "*****************************************************************************\n" )); DEBUG ((DEBUG_INFO, (sizeof(UINTN) == sizeof(UINT64)) ? "DMAR address ............................................. 0x%016lx\n" : "DMAR address ............................................. 0x%08x\n", Dmar )); DEBUG ((DEBUG_INFO, " Table Contents:\n" )); DEBUG ((DEBUG_INFO, " Host Address Width ................................... 0x%02x\n", Dmar->HostAddressWidth )); DEBUG ((DEBUG_INFO, " Flags ................................................ 0x%02x\n", Dmar->Flags )); DEBUG ((DEBUG_INFO, " INTR_REMAP ......................................... 0x%02x\n", Dmar->Flags & EFI_ACPI_DMAR_FLAGS_INTR_REMAP )); DEBUG ((DEBUG_INFO, " X2APIC_OPT_OUT_SET ................................. 0x%02x\n", Dmar->Flags & EFI_ACPI_DMAR_FLAGS_X2APIC_OPT_OUT )); DmarLen = Dmar->Header.Length - sizeof(EFI_ACPI_DMAR_HEADER); DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)(Dmar + 1); while (DmarLen > 0) { switch (DmarHeader->Type) { case EFI_ACPI_DMAR_TYPE_DRHD: DumpDmarDrhd ((EFI_ACPI_DMAR_DRHD_HEADER *)DmarHeader); break; case EFI_ACPI_DMAR_TYPE_RMRR: DumpDmarRmrr ((EFI_ACPI_DMAR_RMRR_HEADER *)DmarHeader); break; default: break; } DmarLen -= DmarHeader->Length; DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length); } DEBUG ((DEBUG_INFO, "*****************************************************************************\n\n" )); return; } /** Get VTd engine number. @param[in] AcpiDmarTable DMAR ACPI table @return the VTd engine number. **/ UINTN GetVtdEngineNumber ( IN EFI_ACPI_DMAR_HEADER *AcpiDmarTable ) { EFI_ACPI_DMAR_STRUCTURE_HEADER *DmarHeader; UINTN VtdIndex; VtdIndex = 0; DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)(AcpiDmarTable + 1)); while ((UINTN)DmarHeader < (UINTN)AcpiDmarTable + AcpiDmarTable->Header.Length) { switch (DmarHeader->Type) { case EFI_ACPI_DMAR_TYPE_DRHD: VtdIndex++; break; default: break; } DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length); } return VtdIndex ; } /** Process DMAR DHRD table. @param[in] VTdInfo The VTd engine context information. @param[in] VtdIndex The index of VTd engine. @param[in] DmarDrhd The DRHD table. **/ VOID ProcessDhrd ( IN VTD_INFO *VTdInfo, IN UINTN VtdIndex, IN EFI_ACPI_DMAR_DRHD_HEADER *DmarDrhd ) { DEBUG ((DEBUG_INFO," VTD (%d) BaseAddress - 0x%016lx\n", VtdIndex, DmarDrhd->RegisterBaseAddress)); VTdInfo->VTdEngineAddress[VtdIndex] = DmarDrhd->RegisterBaseAddress; } /** Parse DMAR DRHD table. @param[in] AcpiDmarTable DMAR ACPI table @return EFI_SUCCESS The DMAR DRHD table is parsed. **/ EFI_STATUS ParseDmarAcpiTableDrhd ( IN EFI_ACPI_DMAR_HEADER *AcpiDmarTable ) { EFI_ACPI_DMAR_STRUCTURE_HEADER *DmarHeader; UINTN VtdUnitNumber; UINTN VtdIndex; VTD_INFO *VTdInfo; VtdUnitNumber = GetVtdEngineNumber (AcpiDmarTable); if (VtdUnitNumber == 0) { return EFI_UNSUPPORTED; } VTdInfo = BuildGuidHob (&mVTdInfoGuid, sizeof(VTD_INFO) + (VtdUnitNumber - 1) * sizeof(UINT64)); ASSERT(VTdInfo != NULL); if (VTdInfo == NULL) { return EFI_OUT_OF_RESOURCES; } // // Initialize the engine mask to all. // VTdInfo->AcpiDmarTable = AcpiDmarTable; VTdInfo->EngineMask = LShiftU64 (1, VtdUnitNumber) - 1; VTdInfo->HostAddressWidth = AcpiDmarTable->HostAddressWidth; VTdInfo->VTdEngineCount = VtdUnitNumber; VtdIndex = 0; DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)(AcpiDmarTable + 1)); while ((UINTN)DmarHeader < (UINTN)AcpiDmarTable + AcpiDmarTable->Header.Length) { switch (DmarHeader->Type) { case EFI_ACPI_DMAR_TYPE_DRHD: ASSERT (VtdIndex < VtdUnitNumber); ProcessDhrd (VTdInfo, VtdIndex, (EFI_ACPI_DMAR_DRHD_HEADER *)DmarHeader); VtdIndex++; break; default: break; } DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length); } ASSERT (VtdIndex == VtdUnitNumber); return EFI_SUCCESS; } /** Return the VTd engine index according to the Segment and DevScopeEntry. @param AcpiDmarTable DMAR ACPI table @param Segment The segment of the VTd engine @param DevScopeEntry The DevScopeEntry of the VTd engine @return The VTd engine index according to the Segment and DevScopeEntry. @retval -1 The VTd engine is not found. **/ UINTN GetVTdEngineFromDevScopeEntry ( IN EFI_ACPI_DMAR_HEADER *AcpiDmarTable, IN UINT16 Segment, IN EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *DevScopeEntry ) { EFI_ACPI_DMAR_STRUCTURE_HEADER *DmarHeader; UINTN VtdIndex; EFI_ACPI_DMAR_DRHD_HEADER *DmarDrhd; EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *ThisDevScopeEntry; VtdIndex = 0; DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)(AcpiDmarTable + 1)); while ((UINTN)DmarHeader < (UINTN)AcpiDmarTable + AcpiDmarTable->Header.Length) { switch (DmarHeader->Type) { case EFI_ACPI_DMAR_TYPE_DRHD: DmarDrhd = (EFI_ACPI_DMAR_DRHD_HEADER *)DmarHeader; if (DmarDrhd->SegmentNumber != Segment) { // Mismatch break; } if ((DmarDrhd->Header.Length == sizeof(EFI_ACPI_DMAR_DRHD_HEADER)) || ((DmarDrhd->Flags & EFI_ACPI_DMAR_DRHD_FLAGS_INCLUDE_PCI_ALL) != 0)) { // No DevScopeEntry // Do not handle PCI_ALL break; } ThisDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)(DmarDrhd + 1)); while ((UINTN)ThisDevScopeEntry < (UINTN)DmarDrhd + DmarDrhd->Header.Length) { if ((ThisDevScopeEntry->Length == DevScopeEntry->Length) && (CompareMem (ThisDevScopeEntry, DevScopeEntry, DevScopeEntry->Length) == 0)) { return VtdIndex; } ThisDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)ThisDevScopeEntry + ThisDevScopeEntry->Length); } break; default: break; } DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length); } return (UINTN)-1; } /** Process DMAR RMRR table. @param[in] VTdInfo The VTd engine context information. @param[in] DmarRmrr The RMRR table. **/ VOID ProcessRmrr ( IN VTD_INFO *VTdInfo, IN EFI_ACPI_DMAR_RMRR_HEADER *DmarRmrr ) { EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *DmarDevScopeEntry; UINTN VTdIndex; UINT64 RmrrMask; UINTN LowBottom; UINTN LowTop; UINTN HighBottom; UINT64 HighTop; EFI_ACPI_DMAR_HEADER *AcpiDmarTable; AcpiDmarTable = VTdInfo->AcpiDmarTable; DEBUG ((DEBUG_INFO," RMRR (Base 0x%016lx, Limit 0x%016lx)\n", DmarRmrr->ReservedMemoryRegionBaseAddress, DmarRmrr->ReservedMemoryRegionLimitAddress)); if ((DmarRmrr->ReservedMemoryRegionBaseAddress == 0) || (DmarRmrr->ReservedMemoryRegionLimitAddress == 0)) { return ; } DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)(DmarRmrr + 1)); while ((UINTN)DmarDevScopeEntry < (UINTN)DmarRmrr + DmarRmrr->Header.Length) { ASSERT (DmarDevScopeEntry->Type == EFI_ACPI_DEVICE_SCOPE_ENTRY_TYPE_PCI_ENDPOINT); VTdIndex = GetVTdEngineFromDevScopeEntry (AcpiDmarTable, DmarRmrr->SegmentNumber, DmarDevScopeEntry); if (VTdIndex != (UINTN)-1) { RmrrMask = LShiftU64 (1, VTdIndex); LowBottom = 0; LowTop = (UINTN)DmarRmrr->ReservedMemoryRegionBaseAddress; HighBottom = (UINTN)DmarRmrr->ReservedMemoryRegionLimitAddress + 1; HighTop = GetTopMemory (); SetDmaProtectedRange ( VTdInfo, RmrrMask, 0, (UINT32)(LowTop - LowBottom), HighBottom, HighTop - HighBottom ); // // Remove the engine from the engine mask. // The assumption is that any other PEI driver does not access // the device covered by this engine. // VTdInfo->EngineMask = VTdInfo->EngineMask & (~RmrrMask); } DmarDevScopeEntry = (EFI_ACPI_DMAR_DEVICE_SCOPE_STRUCTURE_HEADER *)((UINTN)DmarDevScopeEntry + DmarDevScopeEntry->Length); } } /** Parse DMAR DRHD table. @param[in] VTdInfo The VTd engine context information. **/ VOID ParseDmarAcpiTableRmrr ( IN VTD_INFO *VTdInfo ) { EFI_ACPI_DMAR_HEADER *AcpiDmarTable; EFI_ACPI_DMAR_STRUCTURE_HEADER *DmarHeader; AcpiDmarTable = VTdInfo->AcpiDmarTable; DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)(AcpiDmarTable + 1)); while ((UINTN)DmarHeader < (UINTN)AcpiDmarTable + AcpiDmarTable->Header.Length) { switch (DmarHeader->Type) { case EFI_ACPI_DMAR_TYPE_RMRR: ProcessRmrr (VTdInfo, (EFI_ACPI_DMAR_RMRR_HEADER *)DmarHeader); break; default: break; } DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length); } }