/** @file
Library to get Global VTd PMR alignment information.
Copyright (c) 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef union {
struct {
UINT32 Low;
UINT32 High;
} Data32;
UINT64 Data;
} UINT64_STRUCT;
/**
Get the protected low memory alignment.
@param HostAddressWidth The host address width.
@param VtdUnitBaseAddress The base address of the VTd engine.
@return protected low memory alignment. Ex: 0x100000
**/
UINT32
GetGlobalVTdPlmrAlignment (
IN UINT8 HostAddressWidth,
IN UINTN VtdUnitBaseAddress
)
{
UINT32 Data32;
MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG, 0xFFFFFFFF);
Data32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG);
Data32 = ~Data32 + 1;
return Data32;
}
/**
Get the protected high memory alignment.
@param HostAddressWidth The host address width.
@param VtdUnitBaseAddress The base address of the VTd engine.
@return protected high memory alignment. Ex: 0x100000
**/
UINT64_STRUCT
GetGlobalVTdPhmrAlignment (
IN UINT8 HostAddressWidth,
IN UINTN VtdUnitBaseAddress
)
{
UINT64_STRUCT Data64;
MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG, 0xFFFFFFFFFFFFFFFF);
Data64.Data = MmioRead64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG);
Data64.Data = ~Data64.Data + 1;
Data64.Data = Data64.Data & (LShiftU64 (1, HostAddressWidth) - 1);
return Data64;
}
/**
Get the global VT-d protected memory alignment.
@return The maximum protected memory alignment. Ex: 0x100000
**/
UINTN
EFIAPI
GetGlobalVtdPmrAlignment (
)
{
UINT32 LowMemoryAlignment;
UINT64_STRUCT HighMemoryAlignment;
UINTN MemoryAlignment;
UINT32 GlobalVTdBaseAddress;
EFI_STATUS Status;
UINTN VtdIndex;
EFI_ACPI_DMAR_STRUCTURE_HEADER *DmarHeader;
EFI_ACPI_DMAR_DRHD_HEADER *DrhdHeader;
EFI_ACPI_DMAR_HEADER *AcpiDmarTable;
//
// Initialization
//
GlobalVTdBaseAddress = 0xFFFFFFFF;
LowMemoryAlignment = 0;
HighMemoryAlignment.Data = 0;
MemoryAlignment = 0;
Status = EFI_UNSUPPORTED;
VtdIndex = 0;
DmarHeader = NULL;
DrhdHeader = NULL;
AcpiDmarTable = NULL;
//
// Fetch the PEI DMAR ACPI Table that created and installed in PlatformVTdInfoSamplePei.c
//
Status = PeiServicesLocatePpi (
&gEdkiiVTdInfoPpiGuid,
0,
NULL,
(VOID **)&AcpiDmarTable
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "PeiServicesLocatePpi gEdkiiVTdInfoPpiGuid failed\n"));
Status = EFI_NOT_FOUND;
MemoryAlignment = SIZE_1MB;
} else {
//
// Seatch the DRHD structure with INCLUDE_PCI_ALL flag Set -> Global VT-d
//
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:
DrhdHeader = (EFI_ACPI_DMAR_DRHD_HEADER *) DmarHeader;
if ((DrhdHeader->Flags & EFI_ACPI_DMAR_DRHD_FLAGS_INCLUDE_PCI_ALL) == EFI_ACPI_DMAR_DRHD_FLAGS_INCLUDE_PCI_ALL) {
GlobalVTdBaseAddress = (UINT32)DrhdHeader->RegisterBaseAddress;
DEBUG ((DEBUG_INFO," GlobalVTdBaseAddress: %x\n", GlobalVTdBaseAddress));
}
VtdIndex++;
break;
default:
break;
}
DmarHeader = (EFI_ACPI_DMAR_STRUCTURE_HEADER *)((UINTN)DmarHeader + DmarHeader->Length);
}
if (GlobalVTdBaseAddress == 0xFFFFFFFF) {
DEBUG ((DEBUG_ERROR, "Error! Please set INCLUDE_PCI_ALL flag to your Global VT-d\n"));
MemoryAlignment = SIZE_1MB;
} else {
//
// Get the alignment information from VT-d register
//
LowMemoryAlignment = GetGlobalVTdPlmrAlignment (AcpiDmarTable->HostAddressWidth, GlobalVTdBaseAddress);
HighMemoryAlignment = GetGlobalVTdPhmrAlignment (AcpiDmarTable->HostAddressWidth, GlobalVTdBaseAddress);
if (LowMemoryAlignment < HighMemoryAlignment.Data) {
MemoryAlignment = (UINTN)HighMemoryAlignment.Data;
} else {
MemoryAlignment = LowMemoryAlignment;
}
}
}
return MemoryAlignment;
}