Remove memory discovered dependency to support both premem VTD_INFO_PPI and postmem VTD_INFO_PPI. If VTD_INFO_PPI is installed before memory is ready, this driver protects all memory region. If VTD_INFO_PPI is installed or reinstalled after memory is ready, this driver allocates DMA buffer and protect rest. Cc: Star Zeng <star.zeng@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Jiewen Yao <jiewen.yao@intel.com>
		
			
				
	
	
		
			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;
 | |
| }
 |