/** @file Copyright (c) 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include "IntelVTdDmarPei.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] VTdUnitInfo The VTd engine unit information. @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 VTD_UNIT_INFO *VTdUnitInfo, IN UINTN Base, IN UINTN Size ) { if (VTdUnitInfo->ECapReg.Bits.C == 0) { WriteBackDataCacheRange ((VOID *) Base, Size); } } /** Flush VTd engine write buffer. @param[in] 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); } } /** Perpare cache invalidation interface. @param[in] VTdUnitInfo The VTd engine unit information. @retval EFI_SUCCESS The operation was successful. @retval EFI_UNSUPPORTED Invalidation method is not supported. @retval EFI_OUT_OF_RESOURCES A memory allocation failed. **/ EFI_STATUS PerpareCacheInvalidationInterface ( IN VTD_UNIT_INFO *VTdUnitInfo ) { UINT16 QueueSize; UINT64 Reg64; UINT32 Reg32; VTD_ECAP_REG ECapReg; if (VTdUnitInfo->VerReg.Bits.Major <= 6) { VTdUnitInfo->EnableQueuedInvalidation = 0; DEBUG ((DEBUG_INFO, "Use Register-based Invalidation Interface for engine [0x%x]\n", VTdUnitInfo->VtdUnitBaseAddress)); return EFI_SUCCESS; } ECapReg.Uint64 = MmioRead64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_ECAP_REG); if (ECapReg.Bits.QI == 0) { DEBUG ((DEBUG_ERROR, "Hardware does not support queued invalidations interface for engine [0x%x]\n", VTdUnitInfo->VtdUnitBaseAddress)); return EFI_UNSUPPORTED; } VTdUnitInfo->EnableQueuedInvalidation = 1; DEBUG ((DEBUG_INFO, "Use Queued Invalidation Interface for engine [0x%x]\n", VTdUnitInfo->VtdUnitBaseAddress)); Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); if ((Reg32 & B_GSTS_REG_QIES) != 0) { DEBUG ((DEBUG_INFO,"Queued Invalidation Interface was enabled.\n")); Reg32 &= (~B_GSTS_REG_QIES); MmioWrite32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GCMD_REG, Reg32); do { Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); } while ((Reg32 & B_GSTS_REG_QIES) != 0); MmioWrite64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_IQA_REG, 0); if (VTdUnitInfo->QiDesc != NULL) { FreePages(VTdUnitInfo->QiDesc, EFI_SIZE_TO_PAGES(sizeof(QI_DESC) * VTdUnitInfo->QiDescLength)); VTdUnitInfo->QiDesc = NULL; VTdUnitInfo->QiDescLength = 0; } } // // Initialize the Invalidation Queue Tail Register to zero. // MmioWrite64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_IQT_REG, 0); // // Setup the IQ address, size and descriptor width through the Invalidation Queue Address Register // QueueSize = 0; VTdUnitInfo->QiDescLength = 1 << (QueueSize + 8); VTdUnitInfo->QiDesc = (QI_DESC *) AllocatePages (EFI_SIZE_TO_PAGES(sizeof(QI_DESC) * VTdUnitInfo->QiDescLength)); if (VTdUnitInfo->QiDesc == NULL) { VTdUnitInfo->QiDescLength = 0; DEBUG ((DEBUG_ERROR,"Could not Alloc Invalidation Queue Buffer.\n")); return EFI_OUT_OF_RESOURCES; } DEBUG ((DEBUG_INFO, "Invalidation Queue Length : %d\n", VTdUnitInfo->QiDescLength)); Reg64 = (UINT64)(UINTN)VTdUnitInfo->QiDesc; Reg64 |= QueueSize; MmioWrite64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_IQA_REG, Reg64); // // Enable the queued invalidation interface through the Global Command Register. // When enabled, hardware sets the QIES field in the Global Status Register. // Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); Reg32 |= B_GMCD_REG_QIE; MmioWrite32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GCMD_REG, Reg32); DEBUG ((DEBUG_INFO, "Enable Queued Invalidation Interface. GCMD_REG = 0x%x\n", Reg32)); do { Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); } while ((Reg32 & B_GSTS_REG_QIES) == 0); VTdUnitInfo->QiFreeHead = 0; return EFI_SUCCESS; } /** Disable queued invalidation interface. @param[in] VTdUnitInfo The VTd engine unit information. **/ VOID DisableQueuedInvalidationInterface ( IN VTD_UNIT_INFO *VTdUnitInfo ) { UINT32 Reg32; if (VTdUnitInfo->EnableQueuedInvalidation != 0) { Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); Reg32 &= (~B_GMCD_REG_QIE); MmioWrite32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GCMD_REG, Reg32); DEBUG ((DEBUG_INFO, "Disable Queued Invalidation Interface. GCMD_REG = 0x%x\n", Reg32)); do { Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); } while ((Reg32 & B_GSTS_REG_QIES) != 0); if (VTdUnitInfo->QiDesc != NULL) { FreePages(VTdUnitInfo->QiDesc, EFI_SIZE_TO_PAGES(sizeof(QI_DESC) * VTdUnitInfo->QiDescLength)); VTdUnitInfo->QiDesc = NULL; VTdUnitInfo->QiDescLength = 0; } VTdUnitInfo->EnableQueuedInvalidation = 0; } } /** Check Queued Invalidation Fault. @param[in] VTdUnitInfo The VTd engine unit information. @retval EFI_SUCCESS The operation was successful. @retval RETURN_DEVICE_ERROR A fault is detected. **/ EFI_STATUS QueuedInvalidationCheckFault ( IN VTD_UNIT_INFO *VTdUnitInfo ) { UINT32 FaultReg; FaultReg = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_FSTS_REG); if (FaultReg & B_FSTS_REG_IQE) { DEBUG((DEBUG_ERROR, "Detect Invalidation Queue Error [0x%08x]\n", FaultReg)); FaultReg |= B_FSTS_REG_IQE; MmioWrite32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_FSTS_REG, FaultReg); return RETURN_DEVICE_ERROR; } if (FaultReg & B_FSTS_REG_ITE) { DEBUG((DEBUG_ERROR, "Detect Invalidation Time-out Error [0x%08x]\n", FaultReg)); FaultReg |= B_FSTS_REG_ITE; MmioWrite32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_FSTS_REG, FaultReg); return RETURN_DEVICE_ERROR; } if (FaultReg & B_FSTS_REG_ICE) { DEBUG((DEBUG_ERROR, "Detect Invalidation Completion Error [0x%08x]\n", FaultReg)); FaultReg |= B_FSTS_REG_ICE; MmioWrite32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_FSTS_REG, FaultReg); return RETURN_DEVICE_ERROR; } return EFI_SUCCESS; } /** Submit the queued invalidation descriptor to the remapping hardware unit and wait for its completion. @param[in] VTdUnitInfo The VTd engine unit information. @param[in] Desc The invalidate descriptor @retval EFI_SUCCESS The operation was successful. @retval RETURN_DEVICE_ERROR A fault is detected. @retval EFI_INVALID_PARAMETER Parameter is invalid. **/ EFI_STATUS SubmitQueuedInvalidationDescriptor ( IN VTD_UNIT_INFO *VTdUnitInfo, IN QI_DESC *Desc ) { EFI_STATUS Status; UINT16 QiDescLength; QI_DESC *BaseDesc; UINT64 Reg64Iqt; UINT64 Reg64Iqh; if (Desc == NULL) { return EFI_INVALID_PARAMETER; } QiDescLength = VTdUnitInfo->QiDescLength; BaseDesc = VTdUnitInfo->QiDesc; DEBUG((DEBUG_INFO, "[0x%x] Submit QI Descriptor [0x%08x, 0x%08x]\n", VTdUnitInfo->VtdUnitBaseAddress, Desc->Low, Desc->High)); BaseDesc[VTdUnitInfo->QiFreeHead].Low = Desc->Low; BaseDesc[VTdUnitInfo->QiFreeHead].High = Desc->High; FlushPageTableMemory(VTdUnitInfo, (UINTN) &BaseDesc[VTdUnitInfo->QiFreeHead], sizeof(QI_DESC)); DEBUG((DEBUG_INFO,"QI Free Head=0x%x\n", VTdUnitInfo->QiFreeHead)); VTdUnitInfo->QiFreeHead = (VTdUnitInfo->QiFreeHead + 1) % QiDescLength; Reg64Iqh = MmioRead64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_IQH_REG); // // Update the HW tail register indicating the presence of new descriptors. // Reg64Iqt = VTdUnitInfo->QiFreeHead << DMAR_IQ_SHIFT; MmioWrite64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_IQT_REG, Reg64Iqt); Status = EFI_SUCCESS; do { Status = QueuedInvalidationCheckFault(VTdUnitInfo); if (Status != EFI_SUCCESS) { DEBUG((DEBUG_ERROR,"Detect Queued Invalidation Fault.\n")); break; } Reg64Iqh = MmioRead64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_IQH_REG); } while (Reg64Iqt != Reg64Iqh); DEBUG((DEBUG_ERROR,"SubmitQueuedInvalidationDescriptor end\n")); return Status; } /** Invalidate VTd context cache. @param[in] VTdUnitInfo The VTd engine unit information. **/ EFI_STATUS InvalidateContextCache ( IN VTD_UNIT_INFO *VTdUnitInfo ) { UINT64 Reg64; QI_DESC QiDesc; if (VTdUnitInfo->EnableQueuedInvalidation == 0) { // // Register-based Invalidation // Reg64 = MmioRead64 ((UINTN)VTdUnitInfo->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", (UINTN)VTdUnitInfo->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 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_CCMD_REG, Reg64); do { Reg64 = MmioRead64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_CCMD_REG); } while ((Reg64 & B_CCMD_REG_ICC) != 0); } else { // // Queued Invalidation // QiDesc.Low = QI_CC_FM(0) | QI_CC_SID(0) | QI_CC_DID(0) | QI_CC_GRAN(1) | QI_CC_TYPE; QiDesc.High = 0; return SubmitQueuedInvalidationDescriptor(VTdUnitInfo, &QiDesc); } return EFI_SUCCESS; } /** Invalidate VTd IOTLB. @param[in] VTdUnitInfo The VTd engine unit information. **/ EFI_STATUS InvalidateIOTLB ( IN VTD_UNIT_INFO *VTdUnitInfo ) { UINT64 Reg64; VTD_ECAP_REG ECapReg; QI_DESC QiDesc; if (VTdUnitInfo->EnableQueuedInvalidation == 0) { // // Register-based Invalidation // ECapReg.Uint64 = MmioRead64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_ECAP_REG); Reg64 = MmioRead64 ((UINTN)VTdUnitInfo->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", (UINTN)VTdUnitInfo->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 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + (ECapReg.Bits.IRO * 16) + R_IOTLB_REG, Reg64); do { Reg64 = MmioRead64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + (ECapReg.Bits.IRO * 16) + R_IOTLB_REG); } while ((Reg64 & B_IOTLB_REG_IVT) != 0); } else { // // Queued Invalidation // ECapReg.Uint64 = MmioRead64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_ECAP_REG); QiDesc.Low = QI_IOTLB_DID(0) | QI_IOTLB_DR(CAP_READ_DRAIN(ECapReg.Uint64)) | QI_IOTLB_DW(CAP_WRITE_DRAIN(ECapReg.Uint64)) | QI_IOTLB_GRAN(1) | QI_IOTLB_TYPE; QiDesc.High = QI_IOTLB_ADDR(0) | QI_IOTLB_IH(0) | QI_IOTLB_AM(0); return SubmitQueuedInvalidationDescriptor(VTdUnitInfo, &QiDesc); } return EFI_SUCCESS; } /** Enable DMAR translation inpre-mem phase. @param[in] VtdUnitBaseAddress The base address of the VTd engine. @param[in] 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 EnableDmarPreMem ( IN UINTN VtdUnitBaseAddress, IN UINTN RootEntryTable ) { UINT32 Reg32; DEBUG ((DEBUG_INFO, ">>>>>>EnableDmarPreMem() for engine [%x] \n", VtdUnitBaseAddress)); DEBUG ((DEBUG_INFO, "RootEntryTable 0x%x \n", RootEntryTable)); MmioWrite64 (VtdUnitBaseAddress + R_RTADDR_REG, (UINT64) (UINTN) RootEntryTable); Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG); MmioWrite32 (VtdUnitBaseAddress + R_GCMD_REG, Reg32 | B_GMCD_REG_SRTP); DEBUG ((DEBUG_INFO, "EnableDmarPreMem: waiting for RTPS bit to be set... \n")); do { Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG); } while((Reg32 & B_GSTS_REG_RTPS) == 0); DEBUG ((DEBUG_INFO, "EnableDmarPreMem: R_GSTS_REG = 0x%x \n", Reg32)); // // Init DMAr Fault Event and Data registers // Reg32 = MmioRead32 (VtdUnitBaseAddress + R_FEDATA_REG); // // Write Buffer Flush before invalidation // FlushWriteBuffer (VtdUnitBaseAddress); // // Enable VTd // Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG); MmioWrite32 (VtdUnitBaseAddress + R_GCMD_REG, Reg32 | B_GMCD_REG_TE); DEBUG ((DEBUG_INFO, "EnableDmarPreMem: 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; } /** Enable DMAR translation. @param[in] VTdUnitInfo The VTd engine unit information. @param[in] 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 VTD_UNIT_INFO *VTdUnitInfo, IN UINTN RootEntryTable ) { UINT32 Reg32; DEBUG ((DEBUG_INFO, ">>>>>>EnableDmar() for engine [%x] \n", VTdUnitInfo->VtdUnitBaseAddress)); DEBUG ((DEBUG_INFO, "RootEntryTable 0x%x \n", RootEntryTable)); MmioWrite64 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_RTADDR_REG, (UINT64) (UINTN) RootEntryTable); Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); MmioWrite32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GCMD_REG, Reg32 | B_GMCD_REG_SRTP); DEBUG ((DEBUG_INFO, "EnableDmar: waiting for RTPS bit to be set... \n")); do { Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); } while((Reg32 & B_GSTS_REG_RTPS) == 0); DEBUG ((DEBUG_INFO, "EnableDmar: R_GSTS_REG = 0x%x \n", Reg32)); // // Init DMAr Fault Event and Data registers // Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_FEDATA_REG); // // Write Buffer Flush before invalidation // FlushWriteBuffer ((UINTN)VTdUnitInfo->VtdUnitBaseAddress); // // Invalidate the context cache // InvalidateContextCache (VTdUnitInfo); // // Invalidate the IOTLB cache // InvalidateIOTLB (VTdUnitInfo); // // Enable VTd // Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); MmioWrite32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GCMD_REG, Reg32 | B_GMCD_REG_TE); DEBUG ((DEBUG_INFO, "EnableDmar: Waiting B_GSTS_REG_TE ...\n")); do { Reg32 = MmioRead32 ((UINTN)VTdUnitInfo->VtdUnitBaseAddress + R_GSTS_REG); } while ((Reg32 & B_GSTS_REG_TE) == 0); DEBUG ((DEBUG_INFO, "VTD () enabled!<<<<<<\n")); return EFI_SUCCESS; } /** Disable DMAR translation. @param[in] 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; UINT32 Status; UINT32 Command; DEBUG ((DEBUG_INFO, ">>>>>>DisableDmar() for engine [%x] \n", VtdUnitBaseAddress)); // // Write Buffer Flush before invalidation // FlushWriteBuffer (VtdUnitBaseAddress); // // Disable Dmar // // // Set TE (Translation Enable: BIT31) of Global command register to zero // Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG); Status = (Reg32 & 0x96FFFFFF); // Reset the one-shot bits Command = (Status & ~B_GMCD_REG_TE); MmioWrite32 (VtdUnitBaseAddress + R_GCMD_REG, Command); // // Poll on TE Status bit of Global status register to become zero // do { Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG); } while ((Reg32 & B_GSTS_REG_TE) == B_GSTS_REG_TE); // // Set SRTP (Set Root Table Pointer: BIT30) of Global command register in order to update the root table pointerDisable VTd // Reg32 = MmioRead32 (VtdUnitBaseAddress + R_GSTS_REG); Status = (Reg32 & 0x96FFFFFF); // Reset the one-shot bits Command = (Status | B_GMCD_REG_SRTP); MmioWrite32 (VtdUnitBaseAddress + R_GCMD_REG, Command); 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; } /** Dump VTd version registers. @param[in] VerReg The version register. **/ VOID DumpVtdVerRegs ( IN VTD_VER_REG *VerReg ) { DEBUG ((DEBUG_INFO, " VerReg:\n", VerReg->Uint32)); DEBUG ((DEBUG_INFO, " Major - 0x%x\n", VerReg->Bits.Major)); DEBUG ((DEBUG_INFO, " Minor - 0x%x\n", VerReg->Bits.Minor)); } /** Dump VTd capability registers. @param[in] CapReg The capability register. **/ VOID DumpVtdCapRegs ( IN VTD_CAP_REG *CapReg ) { DEBUG ((DEBUG_INFO, " CapReg:\n", CapReg->Uint64)); DEBUG ((DEBUG_INFO, " ND - 0x%x\n", CapReg->Bits.ND)); DEBUG ((DEBUG_INFO, " AFL - 0x%x\n", CapReg->Bits.AFL)); DEBUG ((DEBUG_INFO, " RWBF - 0x%x\n", CapReg->Bits.RWBF)); DEBUG ((DEBUG_INFO, " PLMR - 0x%x\n", CapReg->Bits.PLMR)); DEBUG ((DEBUG_INFO, " PHMR - 0x%x\n", CapReg->Bits.PHMR)); DEBUG ((DEBUG_INFO, " CM - 0x%x\n", CapReg->Bits.CM)); DEBUG ((DEBUG_INFO, " SAGAW - 0x%x\n", CapReg->Bits.SAGAW)); DEBUG ((DEBUG_INFO, " MGAW - 0x%x\n", CapReg->Bits.MGAW)); DEBUG ((DEBUG_INFO, " ZLR - 0x%x\n", CapReg->Bits.ZLR)); DEBUG ((DEBUG_INFO, " FRO - 0x%x\n", CapReg->Bits.FRO)); DEBUG ((DEBUG_INFO, " SLLPS - 0x%x\n", CapReg->Bits.SLLPS)); DEBUG ((DEBUG_INFO, " PSI - 0x%x\n", CapReg->Bits.PSI)); DEBUG ((DEBUG_INFO, " NFR - 0x%x\n", CapReg->Bits.NFR)); DEBUG ((DEBUG_INFO, " MAMV - 0x%x\n", CapReg->Bits.MAMV)); DEBUG ((DEBUG_INFO, " DWD - 0x%x\n", CapReg->Bits.DWD)); DEBUG ((DEBUG_INFO, " DRD - 0x%x\n", CapReg->Bits.DRD)); DEBUG ((DEBUG_INFO, " FL1GP - 0x%x\n", CapReg->Bits.FL1GP)); DEBUG ((DEBUG_INFO, " PI - 0x%x\n", CapReg->Bits.PI)); } /** Dump VTd extended capability registers. @param[in] ECapReg The extended capability register. **/ VOID DumpVtdECapRegs ( IN VTD_ECAP_REG *ECapReg ) { DEBUG ((DEBUG_INFO, " ECapReg:\n", ECapReg->Uint64)); DEBUG ((DEBUG_INFO, " C - 0x%x\n", ECapReg->Bits.C)); DEBUG ((DEBUG_INFO, " QI - 0x%x\n", ECapReg->Bits.QI)); DEBUG ((DEBUG_INFO, " DT - 0x%x\n", ECapReg->Bits.DT)); DEBUG ((DEBUG_INFO, " IR - 0x%x\n", ECapReg->Bits.IR)); DEBUG ((DEBUG_INFO, " EIM - 0x%x\n", ECapReg->Bits.EIM)); DEBUG ((DEBUG_INFO, " PT - 0x%x\n", ECapReg->Bits.PT)); DEBUG ((DEBUG_INFO, " SC - 0x%x\n", ECapReg->Bits.SC)); DEBUG ((DEBUG_INFO, " IRO - 0x%x\n", ECapReg->Bits.IRO)); DEBUG ((DEBUG_INFO, " MHMV - 0x%x\n", ECapReg->Bits.MHMV)); DEBUG ((DEBUG_INFO, " ECS - 0x%x\n", ECapReg->Bits.ECS)); DEBUG ((DEBUG_INFO, " MTS - 0x%x\n", ECapReg->Bits.MTS)); DEBUG ((DEBUG_INFO, " NEST - 0x%x\n", ECapReg->Bits.NEST)); DEBUG ((DEBUG_INFO, " DIS - 0x%x\n", ECapReg->Bits.DIS)); DEBUG ((DEBUG_INFO, " PASID - 0x%x\n", ECapReg->Bits.PASID)); DEBUG ((DEBUG_INFO, " PRS - 0x%x\n", ECapReg->Bits.PRS)); DEBUG ((DEBUG_INFO, " ERS - 0x%x\n", ECapReg->Bits.ERS)); DEBUG ((DEBUG_INFO, " SRS - 0x%x\n", ECapReg->Bits.SRS)); DEBUG ((DEBUG_INFO, " NWFS - 0x%x\n", ECapReg->Bits.NWFS)); DEBUG ((DEBUG_INFO, " EAFS - 0x%x\n", ECapReg->Bits.EAFS)); DEBUG ((DEBUG_INFO, " PSS - 0x%x\n", ECapReg->Bits.PSS)); } /** Enable VTd translation table protection for all. @param[in] VTdInfo The VTd engine context information. @param[in] EngineMask The mask of the VTd engine to be accessed. **/ VOID EnableVTdTranslationProtectionAll ( IN VTD_INFO *VTdInfo, IN UINT64 EngineMask ) { EFI_STATUS Status; EDKII_VTD_NULL_ROOT_ENTRY_TABLE_PPI *RootEntryTable; UINTN Index; DEBUG ((DEBUG_INFO, "EnableVTdTranslationProtectionAll - 0x%lx\n", EngineMask)); Status = PeiServicesLocatePpi ( &gEdkiiVTdNullRootEntryTableGuid, 0, NULL, (VOID **)&RootEntryTable ); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_ERROR, "Locate Null Root Entry Table Ppi Failed : %r\n", Status)); ASSERT (FALSE); return; } for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) { if ((EngineMask & LShiftU64(1, Index)) == 0) { continue; } VTdInfo->VtdUnitInfo[Index].VerReg.Uint32 = MmioRead32 (VTdInfo->VtdUnitInfo[Index].VtdUnitBaseAddress + R_VER_REG); DumpVtdVerRegs (&VTdInfo->VtdUnitInfo[Index].VerReg); VTdInfo->VtdUnitInfo[Index].CapReg.Uint64 = MmioRead64 (VTdInfo->VtdUnitInfo[Index].VtdUnitBaseAddress + R_CAP_REG); DumpVtdCapRegs (&VTdInfo->VtdUnitInfo[Index].CapReg); VTdInfo->VtdUnitInfo[Index].ECapReg.Uint64 = MmioRead64 (VTdInfo->VtdUnitInfo[Index].VtdUnitBaseAddress + R_ECAP_REG); DumpVtdECapRegs (&VTdInfo->VtdUnitInfo[Index].ECapReg); EnableDmarPreMem (VTdInfo->VtdUnitInfo[Index].VtdUnitBaseAddress, (UINTN) *RootEntryTable); } return; } /** Enable VTd translation table protection. @param[in] VTdInfo The VTd engine context information. @retval EFI_SUCCESS DMAR translation is enabled. @retval EFI_DEVICE_ERROR DMAR translation is not enabled. **/ EFI_STATUS EnableVTdTranslationProtection ( IN VTD_INFO *VTdInfo ) { EFI_STATUS Status; UINTN VtdIndex; for (VtdIndex = 0; VtdIndex < VTdInfo->VTdEngineCount; VtdIndex++) { if (VTdInfo->VtdUnitInfo[VtdIndex].ExtRootEntryTable != 0) { DEBUG ((DEBUG_INFO, "EnableVtdDmar (%d) ExtRootEntryTable 0x%x\n", VtdIndex, VTdInfo->VtdUnitInfo[VtdIndex].ExtRootEntryTable)); Status = EnableDmar (&VTdInfo->VtdUnitInfo[VtdIndex], VTdInfo->VtdUnitInfo[VtdIndex].ExtRootEntryTable); } else { DEBUG ((DEBUG_INFO, "EnableVtdDmar (%d) RootEntryTable 0x%x\n", VtdIndex, VTdInfo->VtdUnitInfo[VtdIndex].RootEntryTable)); Status = EnableDmar (&VTdInfo->VtdUnitInfo[VtdIndex], VTdInfo->VtdUnitInfo[VtdIndex].RootEntryTable); } if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "EnableVtdDmar (%d) Failed !\n", VtdIndex)); return Status; } } return EFI_SUCCESS; } /** Disable VTd translation table protection. @param[in] VTdInfo The VTd engine context information. @param[in] 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->VtdUnitInfo[Index].VtdUnitBaseAddress); DisableQueuedInvalidationInterface(&VTdInfo->VtdUnitInfo[Index]); } return; } /** Prepare VTD cache invalidation configuration. @param[in] VTdInfo The VTd engine context information. @retval EFI_SUCCESS Prepare Vtd config success **/ EFI_STATUS PrepareVtdCacheInvalidationConfig ( IN VTD_INFO *VTdInfo ) { UINTN Index; EFI_STATUS Status; for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) { Status = PerpareCacheInvalidationInterface(&VTdInfo->VtdUnitInfo[Index]); if (EFI_ERROR (Status)) { return Status; } } return EFI_SUCCESS; } /** Prepare VTD configuration. @param[in] VTdInfo The VTd engine context information. @retval EFI_SUCCESS Prepare Vtd config success **/ EFI_STATUS PrepareVtdConfig ( IN VTD_INFO *VTdInfo ) { UINTN Index; UINTN DomainNumber; for (Index = 0; Index < VTdInfo->VTdEngineCount; Index++) { DEBUG ((DEBUG_ERROR, "Dump VTd Capability (%d)\n", Index)); VTdInfo->VtdUnitInfo[Index].VerReg.Uint32 = MmioRead32 (VTdInfo->VtdUnitInfo[Index].VtdUnitBaseAddress + R_VER_REG); DumpVtdVerRegs (&VTdInfo->VtdUnitInfo[Index].VerReg); VTdInfo->VtdUnitInfo[Index].CapReg.Uint64 = MmioRead64 (VTdInfo->VtdUnitInfo[Index].VtdUnitBaseAddress + R_CAP_REG); DumpVtdCapRegs (&VTdInfo->VtdUnitInfo[Index].CapReg); VTdInfo->VtdUnitInfo[Index].ECapReg.Uint64 = MmioRead64 (VTdInfo->VtdUnitInfo[Index].VtdUnitBaseAddress + R_ECAP_REG); DumpVtdECapRegs (&VTdInfo->VtdUnitInfo[Index].ECapReg); VTdInfo->VtdUnitInfo[Index].Is5LevelPaging = FALSE; if ((VTdInfo->VtdUnitInfo[Index].CapReg.Bits.SAGAW & BIT2) != 0) { DEBUG ((DEBUG_INFO, "Support 4-level page-table on VTD %d\n", Index)); } if ((VTdInfo->VtdUnitInfo[Index].CapReg.Bits.SAGAW & BIT3) != 0) { DEBUG((DEBUG_INFO, "Support 5-level page-table on VTD %d\n", Index)); VTdInfo->VtdUnitInfo[Index].Is5LevelPaging = TRUE; if ((VTdInfo->HostAddressWidth <= 48) && ((VTdInfo->VtdUnitInfo[Index].CapReg.Bits.SAGAW & BIT2) != 0)) { DEBUG ((DEBUG_INFO, "Rollback to 4-level page-table on VTD %d\n", Index)); VTdInfo->VtdUnitInfo[Index].Is5LevelPaging = FALSE; } } if ((VTdInfo->VtdUnitInfo[Index].CapReg.Bits.SAGAW & (BIT3 | BIT2)) == 0) { DEBUG ((DEBUG_ERROR, "!!!! Page-table type 0x%X is not supported on VTD %d !!!!\n", Index, VTdInfo->VtdUnitInfo[Index].CapReg.Bits.SAGAW)); return EFI_UNSUPPORTED; } DomainNumber = (UINTN)1 << (UINT8) ((UINTN) VTdInfo->VtdUnitInfo[Index].CapReg.Bits.ND * 2 + 4); if (VTdInfo->VtdUnitInfo[Index].PciDeviceInfo.PciDeviceDataNumber >= DomainNumber) { DEBUG ((DEBUG_ERROR, "!!!! Pci device Number(0x%x) >= DomainNumber(0x%x) !!!!\n", VTdInfo->VtdUnitInfo[Index].PciDeviceInfo.PciDeviceDataNumber, DomainNumber)); return EFI_UNSUPPORTED; } } return EFI_SUCCESS; }