mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-11-04 14:48:56 +00:00 
			
		
		
		
	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>
		
			
				
	
	
		
			294 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			294 lines
		
	
	
		
			7.7 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 <Library/MemoryAllocationLib.h>
 | 
						|
#include <Library/CacheMaintenanceLib.h>
 | 
						|
#include <IndustryStandard/Vtd.h>
 | 
						|
#include <Ppi/VtdInfo.h>
 | 
						|
 | 
						|
#include "IntelVTdPmrPei.h"
 | 
						|
 | 
						|
/**
 | 
						|
  Flush VTD page table and context table memory.
 | 
						|
 | 
						|
  This action is to make sure the IOMMU engine can get final data in memory.
 | 
						|
 | 
						|
  @param[in]  Base              The base address of memory to be flushed.
 | 
						|
  @param[in]  Size              The size of memory in bytes to be flushed.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
FlushPageTableMemory (
 | 
						|
  IN UINTN  Base,
 | 
						|
  IN UINTN  Size
 | 
						|
  )
 | 
						|
{
 | 
						|
  WriteBackDataCacheRange ((VOID *)Base, Size);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Flush VTd engine write buffer.
 | 
						|
 | 
						|
  @param VtdUnitBaseAddress The base address of the VTd engine.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
FlushWriteBuffer (
 | 
						|
  IN UINTN  VtdUnitBaseAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32      Reg32;
 | 
						|
  VTD_CAP_REG CapReg;
 | 
						|
 | 
						|
  CapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_CAP_REG);
 | 
						|
 | 
						|
  if (CapReg.Bits.RWBF != 0) {
 | 
						|
    Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG);
 | 
						|
    MmioWrite32 (VtdUnitBaseAddress + R_GCMD_REG, Reg32 | B_GMCD_REG_WBF);
 | 
						|
    do {
 | 
						|
      Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG);
 | 
						|
    } while ((Reg32 & B_GSTS_REG_WBF) != 0);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Invalidate VTd context cache.
 | 
						|
 | 
						|
  @param VtdUnitBaseAddress The base address of the VTd engine.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
InvalidateContextCache (
 | 
						|
  IN UINTN  VtdUnitBaseAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT64  Reg64;
 | 
						|
 | 
						|
  Reg64 = MmioRead64 (VtdUnitBaseAddress + R_CCMD_REG);
 | 
						|
  if ((Reg64 & B_CCMD_REG_ICC) != 0) {
 | 
						|
    DEBUG ((DEBUG_ERROR,"ERROR: InvalidateContextCache: B_CCMD_REG_ICC is set for VTD(%x)\n",VtdUnitBaseAddress));
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  Reg64 &= ((~B_CCMD_REG_ICC) & (~B_CCMD_REG_CIRG_MASK));
 | 
						|
  Reg64 |= (B_CCMD_REG_ICC | V_CCMD_REG_CIRG_GLOBAL);
 | 
						|
  MmioWrite64 (VtdUnitBaseAddress + R_CCMD_REG, Reg64);
 | 
						|
 | 
						|
  do {
 | 
						|
    Reg64 = MmioRead64 (VtdUnitBaseAddress + R_CCMD_REG);
 | 
						|
  } while ((Reg64 & B_CCMD_REG_ICC) != 0);
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Invalidate VTd IOTLB.
 | 
						|
 | 
						|
  @param VtdUnitBaseAddress The base address of the VTd engine.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
InvalidateIOTLB (
 | 
						|
  IN UINTN  VtdUnitBaseAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT64       Reg64;
 | 
						|
  VTD_ECAP_REG ECapReg;
 | 
						|
 | 
						|
  ECapReg.Uint64 = MmioRead64 (VtdUnitBaseAddress + R_ECAP_REG);
 | 
						|
 | 
						|
  Reg64 = MmioRead64 (VtdUnitBaseAddress + (ECapReg.Bits.IRO * 16) + R_IOTLB_REG);
 | 
						|
  if ((Reg64 & B_IOTLB_REG_IVT) != 0) {
 | 
						|
    DEBUG ((DEBUG_ERROR,"ERROR: InvalidateIOTLB: B_IOTLB_REG_IVT is set for VTD(%x)\n", VtdUnitBaseAddress));
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  Reg64 &= ((~B_IOTLB_REG_IVT) & (~B_IOTLB_REG_IIRG_MASK));
 | 
						|
  Reg64 |= (B_IOTLB_REG_IVT | V_IOTLB_REG_IIRG_GLOBAL);
 | 
						|
  MmioWrite64 (VtdUnitBaseAddress + (ECapReg.Bits.IRO * 16) + R_IOTLB_REG, Reg64);
 | 
						|
 | 
						|
  do {
 | 
						|
    Reg64 = MmioRead64 (VtdUnitBaseAddress + (ECapReg.Bits.IRO * 16) + R_IOTLB_REG);
 | 
						|
  } while ((Reg64 & B_IOTLB_REG_IVT) != 0);
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Enable DMAR translation.
 | 
						|
 | 
						|
  @param VtdUnitBaseAddress The base address of the VTd engine.
 | 
						|
  @param RootEntryTable     The address of the VTd RootEntryTable.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           DMAR translation is enabled.
 | 
						|
  @retval EFI_DEVICE_ERROR      DMAR translation is not enabled.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EnableDmar (
 | 
						|
  IN UINTN  VtdUnitBaseAddress,
 | 
						|
  IN UINTN  RootEntryTable
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32    Reg32;
 | 
						|
 | 
						|
  DEBUG((DEBUG_INFO, ">>>>>>EnableDmar() for engine [%x] \n", VtdUnitBaseAddress));
 | 
						|
 | 
						|
  DEBUG((DEBUG_INFO, "RootEntryTable 0x%x \n", RootEntryTable));
 | 
						|
  MmioWrite64 (VtdUnitBaseAddress + R_RTADDR_REG, (UINT64)(UINTN)RootEntryTable);
 | 
						|
 | 
						|
  MmioWrite32 (VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_SRTP);
 | 
						|
 | 
						|
  DEBUG((DEBUG_INFO, "EnableDmar: waiting for RTPS bit to be set... \n"));
 | 
						|
  do {
 | 
						|
    Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG);
 | 
						|
  } while((Reg32 & B_GSTS_REG_RTPS) == 0);
 | 
						|
 | 
						|
  //
 | 
						|
  // Init DMAr Fault Event and Data registers
 | 
						|
  //
 | 
						|
  Reg32 = MmioRead32 (VtdUnitBaseAddress + R_FEDATA_REG);
 | 
						|
 | 
						|
  //
 | 
						|
  // Write Buffer Flush before invalidation
 | 
						|
  //
 | 
						|
  FlushWriteBuffer (VtdUnitBaseAddress);
 | 
						|
 | 
						|
  //
 | 
						|
  // Invalidate the context cache
 | 
						|
  //
 | 
						|
  InvalidateContextCache (VtdUnitBaseAddress);
 | 
						|
 | 
						|
  //
 | 
						|
  // Invalidate the IOTLB cache
 | 
						|
  //
 | 
						|
  InvalidateIOTLB (VtdUnitBaseAddress);
 | 
						|
 | 
						|
  //
 | 
						|
  // Enable VTd
 | 
						|
  //
 | 
						|
  MmioWrite32 (VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_TE);
 | 
						|
  DEBUG((DEBUG_INFO, "EnableDmar: Waiting B_GSTS_REG_TE ...\n"));
 | 
						|
  do {
 | 
						|
    Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG);
 | 
						|
  } while ((Reg32 & B_GSTS_REG_TE) == 0);
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO,"VTD () enabled!<<<<<<\n"));
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Disable DMAR translation.
 | 
						|
 | 
						|
  @param VtdUnitBaseAddress The base address of the VTd engine.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           DMAR translation is disabled.
 | 
						|
  @retval EFI_DEVICE_ERROR      DMAR translation is not disabled.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
DisableDmar (
 | 
						|
  IN UINTN  VtdUnitBaseAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32    Reg32;
 | 
						|
 | 
						|
  DEBUG((DEBUG_INFO, ">>>>>>DisableDmar() for engine [%x] \n", VtdUnitBaseAddress));
 | 
						|
 | 
						|
  //
 | 
						|
  // Write Buffer Flush before invalidation
 | 
						|
  //
 | 
						|
  FlushWriteBuffer (VtdUnitBaseAddress);
 | 
						|
 | 
						|
  //
 | 
						|
  // Disable VTd
 | 
						|
  //
 | 
						|
  MmioWrite32 (VtdUnitBaseAddress + R_GCMD_REG, B_GMCD_REG_SRTP);
 | 
						|
  do {
 | 
						|
    Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG);
 | 
						|
  } while((Reg32 & B_GSTS_REG_RTPS) == 0);
 | 
						|
 | 
						|
  Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG);
 | 
						|
  DEBUG((DEBUG_INFO, "DisableDmar: GSTS_REG - 0x%08x\n", Reg32));
 | 
						|
 | 
						|
  MmioWrite64 (VtdUnitBaseAddress + R_RTADDR_REG, 0);
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO,"VTD () Disabled!<<<<<<\n"));
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Enable VTd translation table protection.
 | 
						|
 | 
						|
  @param VTdInfo            The VTd engine context information.
 | 
						|
  @param EngineMask         The mask of the VTd engine to be accessed.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EnableVTdTranslationProtection (
 | 
						|
  IN VTD_INFO      *VTdInfo,
 | 
						|
  IN UINT64        EngineMask
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN       Index;
 | 
						|
  VOID        *RootEntryTable;
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, "EnableVTdTranslationProtection - 0x%lx\n", EngineMask));
 | 
						|
 | 
						|
  RootEntryTable = AllocatePages (1);
 | 
						|
  ASSERT (RootEntryTable != NULL);
 | 
						|
  if (RootEntryTable == NULL) {
 | 
						|
    DEBUG ((DEBUG_INFO, " EnableVTdTranslationProtection : OutOfResource\n"));
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
 | 
						|
  ZeroMem (RootEntryTable, EFI_PAGES_TO_SIZE(1));
 | 
						|
  FlushPageTableMemory ((UINTN)RootEntryTable, EFI_PAGES_TO_SIZE(1));
 | 
						|
 | 
						|
  for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
 | 
						|
    if ((EngineMask & LShiftU64(1, Index)) == 0) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    EnableDmar ((UINTN)VTdInfo->VTdEngineAddress[Index], (UINTN)RootEntryTable);
 | 
						|
  }
 | 
						|
 | 
						|
  return ;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Disable VTd translation table protection.
 | 
						|
 | 
						|
  @param VTdInfo            The VTd engine context information.
 | 
						|
  @param EngineMask         The mask of the VTd engine to be accessed.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
DisableVTdTranslationProtection (
 | 
						|
  IN VTD_INFO      *VTdInfo,
 | 
						|
  IN UINT64        EngineMask
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN       Index;
 | 
						|
 | 
						|
  DEBUG ((DEBUG_INFO, "DisableVTdTranslationProtection - 0x%lx\n", EngineMask));
 | 
						|
 | 
						|
  for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) {
 | 
						|
    if ((EngineMask & LShiftU64(1, Index)) == 0) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    DisableDmar ((UINTN)VTdInfo->VTdEngineAddress[Index]);
 | 
						|
  }
 | 
						|
 | 
						|
  return ;
 | 
						|
}
 |