mirror of https://github.com/acidanthera/audk.git
427 lines
12 KiB
C
427 lines
12 KiB
C
/** @file
|
|
|
|
Copyright (c) 2017, 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 <PiPei.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/IoLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <IndustryStandard/Vtd.h>
|
|
#include <Ppi/VtdInfo.h>
|
|
|
|
#include "IntelVTdPmrPei.h"
|
|
|
|
/**
|
|
Get protected low memory alignment.
|
|
|
|
@param HostAddressWidth The host address width.
|
|
@param VtdUnitBaseAddress The base address of the VTd engine.
|
|
|
|
@return protected low memory alignment.
|
|
**/
|
|
UINT32
|
|
GetPlmrAlignment (
|
|
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 protected high memory alignment.
|
|
|
|
@param HostAddressWidth The host address width.
|
|
@param VtdUnitBaseAddress The base address of the VTd engine.
|
|
|
|
@return protected high memory alignment.
|
|
**/
|
|
UINT64
|
|
GetPhmrAlignment (
|
|
IN UINT8 HostAddressWidth,
|
|
IN UINTN VtdUnitBaseAddress
|
|
)
|
|
{
|
|
UINT64 Data64;
|
|
|
|
MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG, 0xFFFFFFFFFFFFFFFF);
|
|
Data64 = MmioRead64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG);
|
|
Data64 = ~Data64 + 1;
|
|
Data64 = Data64 & (LShiftU64 (1, HostAddressWidth) - 1);
|
|
|
|
return Data64;
|
|
}
|
|
|
|
/**
|
|
Get protected low memory alignment.
|
|
|
|
@param VTdInfo The VTd engine context information.
|
|
@param EngineMask The mask of the VTd engine to be accessed.
|
|
|
|
@return protected low memory alignment.
|
|
**/
|
|
UINT32
|
|
GetLowMemoryAlignment (
|
|
IN VTD_INFO *VTdInfo,
|
|
IN UINT64 EngineMask
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT32 Alignment;
|
|
UINT32 FinalAlignment;
|
|
|
|
FinalAlignment = 0;
|
|
for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
|
|
if ((EngineMask & LShiftU64(1, Index)) == 0) {
|
|
continue;
|
|
}
|
|
Alignment = GetPlmrAlignment (VTdInfo->HostAddressWidth, (UINTN)VTdInfo->VTdEngineAddress[Index]);
|
|
if (FinalAlignment < Alignment) {
|
|
FinalAlignment = Alignment;
|
|
}
|
|
}
|
|
return FinalAlignment;
|
|
}
|
|
|
|
/**
|
|
Get protected high memory alignment.
|
|
|
|
@param VTdInfo The VTd engine context information.
|
|
@param EngineMask The mask of the VTd engine to be accessed.
|
|
|
|
@return protected high memory alignment.
|
|
**/
|
|
UINT64
|
|
GetHighMemoryAlignment (
|
|
IN VTD_INFO *VTdInfo,
|
|
IN UINT64 EngineMask
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT64 Alignment;
|
|
UINT64 FinalAlignment;
|
|
|
|
FinalAlignment = 0;
|
|
for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
|
|
if ((EngineMask & LShiftU64(1, Index)) == 0) {
|
|
continue;
|
|
}
|
|
Alignment = GetPhmrAlignment (VTdInfo->HostAddressWidth, (UINTN)VTdInfo->VTdEngineAddress[Index]);
|
|
if (FinalAlignment < Alignment) {
|
|
FinalAlignment = Alignment;
|
|
}
|
|
}
|
|
return FinalAlignment;
|
|
}
|
|
|
|
/**
|
|
Enable PMR in the VTd engine.
|
|
|
|
@param VtdUnitBaseAddress The base address of the VTd engine.
|
|
|
|
@retval EFI_SUCCESS The PMR is enabled.
|
|
@retval EFI_UNSUPPORTED The PMR is not supported.
|
|
**/
|
|
EFI_STATUS
|
|
EnablePmr (
|
|
IN UINTN VtdUnitBaseAddress
|
|
)
|
|
{
|
|
UINT32 Reg32;
|
|
VTD_CAP_REG CapReg;
|
|
|
|
DEBUG ((DEBUG_INFO, "EnablePmr - %x\n", VtdUnitBaseAddress));
|
|
|
|
CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);
|
|
if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
|
|
if (Reg32 == 0xFFFFFFFF) {
|
|
DEBUG ((DEBUG_ERROR, "R_PMEN_ENABLE_REG - 0x%x\n", Reg32));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
if ((Reg32 & BIT0) == 0) {
|
|
MmioWrite32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG, BIT31);
|
|
do {
|
|
Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
|
|
} while((Reg32 & BIT0) == 0);
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "EnablePmr - Done\n"));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Disable PMR in the VTd engine.
|
|
|
|
@param VtdUnitBaseAddress The base address of the VTd engine.
|
|
|
|
@retval EFI_SUCCESS The PMR is disabled.
|
|
@retval EFI_UNSUPPORTED The PMR is not supported.
|
|
**/
|
|
EFI_STATUS
|
|
DisablePmr (
|
|
IN UINTN VtdUnitBaseAddress
|
|
)
|
|
{
|
|
UINT32 Reg32;
|
|
VTD_CAP_REG CapReg;
|
|
|
|
CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);
|
|
if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
|
|
if (Reg32 == 0xFFFFFFFF) {
|
|
DEBUG ((DEBUG_ERROR, "R_PMEN_ENABLE_REG - 0x%x\n", Reg32));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
if ((Reg32 & BIT0) != 0) {
|
|
MmioWrite32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG, 0x0);
|
|
do {
|
|
Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
|
|
} while((Reg32 & BIT0) != 0);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Set PMR region in the VTd engine.
|
|
|
|
@param HostAddressWidth The host address width.
|
|
@param VtdUnitBaseAddress The base address of the VTd engine.
|
|
@param LowMemoryBase The protected low memory region base.
|
|
@param LowMemoryLength The protected low memory region length.
|
|
@param HighMemoryBase The protected high memory region base.
|
|
@param HighMemoryLength The protected high memory region length.
|
|
|
|
@retval EFI_SUCCESS The PMR is set to protected region.
|
|
@retval EFI_UNSUPPORTED The PMR is not supported.
|
|
**/
|
|
EFI_STATUS
|
|
SetPmrRegion (
|
|
IN UINT8 HostAddressWidth,
|
|
IN UINTN VtdUnitBaseAddress,
|
|
IN UINT32 LowMemoryBase,
|
|
IN UINT32 LowMemoryLength,
|
|
IN UINT64 HighMemoryBase,
|
|
IN UINT64 HighMemoryLength
|
|
)
|
|
{
|
|
VTD_CAP_REG CapReg;
|
|
UINT32 PlmrAlignment;
|
|
UINT64 PhmrAlignment;
|
|
|
|
DEBUG ((DEBUG_INFO, "VtdUnitBaseAddress - 0x%x\n", VtdUnitBaseAddress));
|
|
|
|
CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);
|
|
if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {
|
|
DEBUG ((DEBUG_ERROR, "PLMR/PHMR unsupported\n"));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
PlmrAlignment = GetPlmrAlignment (HostAddressWidth, VtdUnitBaseAddress);
|
|
DEBUG ((DEBUG_INFO, "PlmrAlignment - 0x%x\n", PlmrAlignment));
|
|
PhmrAlignment = GetPhmrAlignment (HostAddressWidth, VtdUnitBaseAddress);
|
|
DEBUG ((DEBUG_INFO, "PhmrAlignment - 0x%lx\n", PhmrAlignment));
|
|
|
|
if ((LowMemoryBase != ALIGN_VALUE(LowMemoryBase, PlmrAlignment)) ||
|
|
(LowMemoryLength != ALIGN_VALUE(LowMemoryLength, PlmrAlignment)) ||
|
|
(HighMemoryBase != ALIGN_VALUE(HighMemoryBase, PhmrAlignment)) ||
|
|
(HighMemoryLength != ALIGN_VALUE(HighMemoryLength, PhmrAlignment))) {
|
|
DEBUG ((DEBUG_ERROR, "PLMR/PHMR alignment issue\n"));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if (LowMemoryBase == 0 && LowMemoryLength == 0) {
|
|
LowMemoryBase = 0xFFFFFFFF;
|
|
}
|
|
if (HighMemoryBase == 0 && HighMemoryLength == 0) {
|
|
HighMemoryBase = 0xFFFFFFFFFFFFFFFF;
|
|
}
|
|
|
|
MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_BASE_REG, LowMemoryBase);
|
|
MmioWrite32 (VtdUnitBaseAddress + R_PMEN_LOW_LIMITE_REG, LowMemoryBase + LowMemoryLength - 1);
|
|
DEBUG ((DEBUG_INFO, "PLMR set done\n"));
|
|
MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_BASE_REG, HighMemoryBase);
|
|
MmioWrite64 (VtdUnitBaseAddress + R_PMEN_HIGH_LIMITE_REG, HighMemoryBase + HighMemoryLength - 1);
|
|
DEBUG ((DEBUG_INFO, "PHMR set done\n"));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Set DMA protected region.
|
|
|
|
@param VTdInfo The VTd engine context information.
|
|
@param EngineMask The mask of the VTd engine to be accessed.
|
|
@param LowMemoryBase The protected low memory region base.
|
|
@param LowMemoryLength The protected low memory region length.
|
|
@param HighMemoryBase The protected high memory region base.
|
|
@param HighMemoryLength The protected high memory region length.
|
|
|
|
@retval EFI_SUCCESS The DMA protection is set.
|
|
@retval EFI_UNSUPPORTED The DMA protection is not set.
|
|
**/
|
|
EFI_STATUS
|
|
SetDmaProtectedRange (
|
|
IN VTD_INFO *VTdInfo,
|
|
IN UINT64 EngineMask,
|
|
IN UINT32 LowMemoryBase,
|
|
IN UINT32 LowMemoryLength,
|
|
IN UINT64 HighMemoryBase,
|
|
IN UINT64 HighMemoryLength
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
|
|
DEBUG ((DEBUG_INFO, "SetDmaProtectedRange(0x%lx) - [0x%x, 0x%x] [0x%lx, 0x%lx]\n", EngineMask, LowMemoryBase, LowMemoryLength, HighMemoryBase, HighMemoryLength));
|
|
|
|
for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
|
|
if ((EngineMask & LShiftU64(1, Index)) == 0) {
|
|
continue;
|
|
}
|
|
DisablePmr ((UINTN)VTdInfo->VTdEngineAddress[Index]);
|
|
Status = SetPmrRegion (
|
|
VTdInfo->HostAddressWidth,
|
|
(UINTN)VTdInfo->VTdEngineAddress[Index],
|
|
LowMemoryBase,
|
|
LowMemoryLength,
|
|
HighMemoryBase,
|
|
HighMemoryLength
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
Status = EnablePmr ((UINTN)VTdInfo->VTdEngineAddress[Index]);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Diable DMA protection.
|
|
|
|
@param VTdInfo The VTd engine context information.
|
|
@param EngineMask The mask of the VTd engine to be accessed.
|
|
|
|
@retval EFI_SUCCESS DMA protection is disabled.
|
|
**/
|
|
EFI_STATUS
|
|
DisableDmaProtection (
|
|
IN VTD_INFO *VTdInfo,
|
|
IN UINT64 EngineMask
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
|
|
DEBUG ((DEBUG_INFO, "DisableDmaProtection - 0x%lx\n", EngineMask));
|
|
|
|
for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
|
|
DEBUG ((DEBUG_INFO, "Disabling...%d\n", Index));
|
|
|
|
if ((EngineMask & LShiftU64(1, Index)) == 0) {
|
|
continue;
|
|
}
|
|
Status = DisablePmr ((UINTN)VTdInfo->VTdEngineAddress[Index]);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Return if the PMR is enabled.
|
|
|
|
@param VtdUnitBaseAddress The base address of the VTd engine.
|
|
|
|
@retval TRUE PMR is enabled.
|
|
@retval FALSE PMR is disabled or unsupported.
|
|
**/
|
|
BOOLEAN
|
|
IsPmrEnabled (
|
|
IN UINTN VtdUnitBaseAddress
|
|
)
|
|
{
|
|
UINT32 Reg32;
|
|
VTD_CAP_REG CapReg;
|
|
|
|
CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);
|
|
if (CapReg.Bits.PLMR == 0 || CapReg.Bits.PHMR == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
Reg32 = MmioRead32 (VtdUnitBaseAddress + R_PMEN_ENABLE_REG);
|
|
if ((Reg32 & BIT0) == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Return the mask of the VTd engine which is enabled.
|
|
|
|
@param VTdInfo The VTd engine context information.
|
|
@param EngineMask The mask of the VTd engine to be accessed.
|
|
|
|
@return the mask of the VTd engine which is enabled.
|
|
**/
|
|
UINT64
|
|
GetDmaProtectionEnabledEngineMask (
|
|
IN VTD_INFO *VTdInfo,
|
|
IN UINT64 EngineMask
|
|
)
|
|
{
|
|
UINTN Index;
|
|
BOOLEAN Result;
|
|
UINT64 EnabledEngineMask;
|
|
|
|
DEBUG ((DEBUG_INFO, "GetDmaProtectionEnabledEngineMask - 0x%lx\n", EngineMask));
|
|
|
|
EnabledEngineMask = 0;
|
|
for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
|
|
if ((EngineMask & LShiftU64(1, Index)) == 0) {
|
|
continue;
|
|
}
|
|
Result = IsPmrEnabled ((UINTN)VTdInfo->VTdEngineAddress[Index]);
|
|
if (Result) {
|
|
EnabledEngineMask |= LShiftU64(1, Index);
|
|
}
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "EnabledEngineMask - 0x%lx\n", EnabledEngineMask));
|
|
return EnabledEngineMask;
|
|
}
|