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