- Timestamp:
- Jan 21, 2021 8:37:35 AM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 142317
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Bus/DevIommuAmd.cpp
r87340 r87341 46 46 47 47 #ifdef IOMMU_WITH_IOTLBE_CACHE 48 # define IOMMU_IOTLBE_PAGE_SIZE_MAX _4M 49 /** The maximum number of IOTLB entries in our cache implementation. */ 50 # define IOMMU_IOTLBE_MAX (IOMMU_IOTLBE_PAGE_SIZE_MAX / _4K) 48 /** The maximum number of IOTLB entries. */ 49 # define IOMMU_IOTLBE_MAX 512 51 50 /** The mask of bits for the domain ID of the IOTLBE key. */ 52 51 # define IOMMU_IOTLB_DOMAIN_ID_MASK UINT64_C(0xffffff0000000000) … … 169 168 { 170 169 /** The AVL tree core. */ 171 AVL U64NODECORECore;170 AVLRU64NODECORE Core; 172 171 /** List node for the LRU (Least Recently Used) list used for eviction. */ 173 RTLISTNODE NdLru;172 RTLISTNODE NdLru; 174 173 /** The I/O walk result of the translation. */ 175 IOWALKRESULT WalkResult;174 IOWALKRESULT WalkResult; 176 175 } IOTLBE; 177 176 AssertCompileSizeAlignment(IOTLBE, 8); … … 211 210 PIOTLBE paIotlbes; 212 211 /** L2 Cache - Maps [DomainId,Iova] to [IOTLBE]. */ 213 AVL U64TREETreeIotlbe;212 AVLRU64TREE TreeIotlbe; 214 213 /** LRU list anchor for IOTLB entries. */ 215 214 RTLISTANCHOR LstLruIotlbe; … … 547 546 #ifdef IOMMU_WITH_IOTLBE_CACHE 548 547 /** 549 * @callback_method_impl{AVL U64CALLBACK}550 */ 551 static DECLCALLBACK(int) iommuAmd R3DestroyIotlbe(PAVLU64NODECORE pCore, void *pvUser)548 * @callback_method_impl{AVLRU64CALLBACK} 549 */ 550 static DECLCALLBACK(int) iommuAmdDestroyIotlbe(PAVLRU64NODECORE pCore, void *pvUser) 552 551 { 553 552 RT_NOREF2(pCore, pvUser); 554 /* Nothing to do as we will destroy IOTLB entries wholesale later. */553 /* Nothing to do here as we will destroy IOTLB entries wholesale later when required. */ 555 554 return VINF_SUCCESS; 556 555 } … … 605 604 static PIOWALKRESULT iommuAmdIotlbLookup(PIOMMU pThis, uint64_t uDomainId, uint64_t uIova) 606 605 { 606 Assert(!(uIova & X86_PAGE_4K_OFFSET_MASK)); 607 607 608 uint64_t const uKey = iommuAmdIotlbConstructKey(uDomainId, uIova); 608 PIOTLBE pIotlbe = (PIOTLBE)RTAvl U64Get(&pThis->TreeIotlbe, uKey);609 PIOTLBE pIotlbe = (PIOTLBE)RTAvlrU64RangeGet(&pThis->TreeIotlbe, uKey); 609 610 if (pIotlbe) 611 { 612 /* Mark the entry as the most recently used one. */ 613 RTListNodeRemove(&pIotlbe->NdLru); 614 RTListAppend(&pThis->LstLruIotlbe, &pIotlbe->NdLru); 610 615 return &pIotlbe->WalkResult; 616 } 611 617 return NULL; 612 618 } … … 617 623 * 618 624 * @param pThis The IOMMU device state. 619 * @param pWalkResult The I/O page walk result to cache. 620 */ 621 static void iommuAmdIotlbAdd(PIOMMU pThis, PCIOWALKRESULT pWalkResult) 622 { 623 /** @todo Make sure somewhere we don't cache translations whose permissions are 624 * IOMMU_IO_PERM_NONE. */ 625 * @param uIova The I/O virtual address being accessed. 626 * @param pWalkResult The I/O page walk result of the access. 627 */ 628 static void iommuAmdIotlbAdd(PIOMMU pThis, uint64_t uIova, PCIOWALKRESULT pWalkResult) 629 { 630 Assert(!(uIova & X86_PAGE_4K_OFFSET_MASK)); 631 Assert(pWalkResult); 632 Assert(pWalkResult->cShift < 63); 633 Assert(pWalkResult->fIoPerm != IOMMU_IO_PERM_NONE); 634 625 635 /* 626 636 * If the cache is full, evict the last recently used entry. … … 632 642 pIotlbe = RTListRemoveFirst(&pThis->LstLruIotlbe, IOTLBE, NdLru); 633 643 Assert(pIotlbe); 634 RTAvl U64Remove(&pThis->TreeIotlbe, pIotlbe->Core.Key);644 RTAvlrU64Remove(&pThis->TreeIotlbe, pIotlbe->Core.Key); 635 645 --pThis->cCachedIotlbes; 636 646 } … … 646 656 RT_ZERO(*pIotlbe); 647 657 648 /* Update the entry with the results of the page walk. */ 649 pIotlbe->WalkResult = *pWalkResult; 650 651 /* Add the entry to the IOTLB cache. */ 652 RTAvlU64Insert(&pThis->TreeIotlbe, &pIotlbe->Core); 653 654 /* 655 * Add the entry to the -end- of last recently used list signifying that 656 * is the most recently used entry. 657 */ 658 /* Update the entry with the result of the page walk. */ 659 pIotlbe->Core.Key = uIova; 660 pIotlbe->Core.KeyLast = uIova + RT_BIT_64(pWalkResult->cShift) - 1; 661 pIotlbe->WalkResult = *pWalkResult; 662 663 /* Add the entry to the cache. */ 664 RTAvlrU64Insert(&pThis->TreeIotlbe, &pIotlbe->Core); 665 666 /* Mark the entry as the most recently used one. */ 658 667 RTListAppend(&pThis->LstLruIotlbe, &pIotlbe->NdLru); 659 668 } … … 661 670 662 671 /** 663 * Removes the IOTLB entry corresponding to the given domain ID and I/O virtual664 * address.665 *666 * @param pThis The IOMMU device state.667 * @param uDomainId The domain ID.668 * @param uIova The I/O virtual address.669 */670 static void iommuAmdIotlbRemove(PIOMMU pThis, uint16_t uDomainId, uint64_t uIova)671 {672 uint64_t const uKey = iommuAmdIotlbConstructKey(uDomainId, uIova);673 PIOTLBE pIotlbe = (PIOTLBE)RTAvlU64Remove(&pThis->TreeIotlbe, uKey);674 if (pIotlbe)675 {676 RTListNodeRemove(&pIotlbe->NdLru);677 --pThis->cCachedIotlbes;678 RT_BZERO(pIotlbe, sizeof(IOTLBE));679 }680 }681 682 683 /**684 672 * Removes all IOTLB entries from the cache. 685 673 * … … 688 676 static void iommuAmdIotlbRemoveAll(PIOMMU pThis) 689 677 { 690 RTAvlU64Destroy(&pThis->TreeIotlbe, iommuAmdR3DestroyIotlbe, NULL /* pvParam */);691 678 RTListInit(&pThis->LstLruIotlbe); 679 RTAvlrU64Destroy(&pThis->TreeIotlbe, iommuAmdDestroyIotlbe, NULL /* pvParam */); 692 680 pThis->cCachedIotlbes = 0; 681 size_t const cbIotlbes = sizeof(IOTLBE) * IOMMU_IOTLBE_MAX; 682 RT_BZERO(pThis->paIotlbes, cbIotlbes); 693 683 } 694 684 … … 706 696 static void iommuAmdIotlbRemoveRange(PIOMMU pThis, uint16_t uDomainId, uint64_t uIova, uint8_t cShift) 707 697 { 698 Assert(!(uIova & X86_PAGE_4K_OFFSET_MASK)); 699 708 700 /* 709 * Our cache is currently based on 4K pages. Pages larger than this are split into 4K pages 710 * before storing in the cache. So an 8K page will have 2 IOTLB entries, 16K will have 4 etc. 711 * However, our cache capacity is limited (see IOMMU_IOTLBE_MAX). Thus, if the guest uses pages 712 * larger than what our cache can split and hold, we will simply flush the entire cache when 713 * requested to flush a partial range since it exceeds the capacity anyway. 701 * Validate invalidation size. 702 * See AMD spec. 2.2.3 "I/O Page Tables for Host Translations". 714 703 */ 715 bool fFitsInCache; 716 /** @todo Find a more efficient way of checking split sizes? */ 717 if ( cShift == 12 /* 4K */ 718 || cShift == 13 /* 8K */ 719 || cShift == 14 /* 16K */ 720 || cShift == 20 /* 1M */) 721 fFitsInCache = true; 704 if ( cShift == 12 /* 4K */ || cShift == 13 /* 8K */ 705 || cShift == 14 /* 16K */ || cShift == 20 /* 1M */ 706 || cShift == 22 /* 4M */ || cShift == 32 /* 4G */) 707 { 708 /* 709 * We remove all ranges (in our tree) containing the range of I/O virtual addresses requesting 710 * to be invalidated. E.g., if the guest is using 1M pages but requests to invalidate only 8K 711 * we invalidate the entire 1M page. On the other hand, we must handle cross-boundary requests 712 * that spans multiple pages. E.g., if the guest is using 4K pages but requests to invalid 8K, 713 * we would need to invalid two 4K pages. 714 */ 715 uint64_t const uIovaLast = uIova + RT_BIT_64(cShift) - 1; 716 for (;;) 717 { 718 uint64_t const uKey = iommuAmdIotlbConstructKey(uDomainId, uIova); 719 PIOTLBE pIotlbe = (PIOTLBE)RTAvlrU64RangeRemove(&pThis->TreeIotlbe, uKey); 720 if (pIotlbe) 721 { 722 uint64_t const uRangeIovaLast = pIotlbe->Core.KeyLast; 723 RTListNodeRemove(&pIotlbe->NdLru); 724 --pThis->cCachedIotlbes; 725 RT_ZERO(*pIotlbe); 726 if (uIovaLast > uRangeIovaLast) 727 uIova = uRangeIovaLast + 1; 728 else 729 break; 730 } 731 else 732 break; 733 } 734 } 722 735 else 723 fFitsInCache = false; 724 725 if (fFitsInCache) 726 { 727 /* Mask off 4K page offset from the address. */ 728 uIova &= X86_PAGE_4K_OFFSET_MASK; 729 730 /* Remove pages in the given range. */ 731 uint16_t const cPages = (1 << cShift) / X86_PAGE_4K_SHIFT; 732 Assert(cPages <= IOMMU_IOTLBE_MAX); 733 for (uint32_t i = 0; i < cPages; i++) 734 { 735 iommuAmdIotlbRemove(pThis, uDomainId, uIova); 736 uIova += _4K; 737 } 738 } 739 else 736 { 737 /* 738 * The guest provided size is either invalid or exceeds the largest, meaningful page size. 739 * In such situations, we flush the entire cache. 740 */ 740 741 iommuAmdIotlbRemoveAll(pThis); 742 } 741 743 } 742 744 #endif /* IOMMU_WITH_IOTLBE_CACHE */ … … 4790 4792 if (pThis->paIotlbes) 4791 4793 { 4792 RTAvlU64Destroy(&pThis->TreeIotlbe, iommuAmdR3DestroyIotlbe, NULL /* pvParam */); 4793 RTListInit(&pThis->LstLruIotlbe); 4794 iommuAmdIotlbRemoveAll(pThis); 4794 4795 PDMDevHlpMMHeapFree(pDevIns, pThis->paIotlbes); 4795 4796 pThis->paIotlbes = NULL;
Note:
See TracChangeset
for help on using the changeset viewer.