VirtualBox

Changeset 84227 in vbox for trunk/src


Ignore:
Timestamp:
May 9, 2020 6:02:47 PM (5 years ago)
Author:
vboxsync
Message:

AMD IOMMU: bugref:9654 Multi-page I/O page walk bits.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Bus/DevIommuAmd.cpp

    r84225 r84227  
    39643964 * @returns VBox status code.
    39653965 * @param   pDevIns     The IOMMU device instance.
    3966  * @param   uIova       The I/O virtual address to translate.
     3966 * @param   uIova       The I/O virtual address to translate. Must be 4K aligned!
    39673967 * @param   uDevId      The device ID.
    3968  * @param   cbAccess    The size of the access.
    39693968 * @param   fAccess     The access permissions (IOMMU_IO_PERM_XXX). This is the
    39703969 *                      permissions for the access being made.
     
    39763975 * @thread  Any.
    39773976 */
    3978 static int iommuAmdWalkIoPageTables(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, size_t cbAccess, uint8_t fAccess,
    3979                                     PCDTE_T pDte, IOMMUOP enmOp, PIOTLBE_T pIotlbe)
    3980 {
    3981     NOREF(cbAccess);
     3977static int iommuAmdWalkIoPageTables(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, uint8_t fAccess, PCDTE_T pDte,
     3978                                    IOMMUOP enmOp, PIOTLBE_T pIotlbe)
     3979{
    39823980    Assert(pDte->n.u1Valid);
     3981    /* The input I/O virtual address must be 4K page aligned. */
     3982    Assert(!(uIova & X86_PAGE_4K_OFFSET_MASK));
    39833983
    39843984    /* If the translation is not valid, raise an I/O page fault. */
     
    40384038        return VERR_IOMMU_ADDR_TRANSLATION_FAILED;
    40394039    }
     4040
     4041    /** @todo IOMMU: Split the rest of this into a separate function called
     4042     *        iommuAmdWalkIoPageDirectory() and call it for multi-page accesses. We can
     4043     *        avoid re-checking the DTE root-page table entry every time. */
    40404044
    40414045    /* The virtual address bits indexing table. */
     
    41904194    }
    41914195
    4192     return VERR_NOT_IMPLEMENTED;
     4196    /* Shouldn't really get here. */
     4197    return VERR_IOMMU_IPE_3;
    41934198}
    41944199
     
    42024207 * @param   uIova       The I/O virtual address to lookup.
    42034208 * @param   cbAccess    The size of the access.
     4209 * @param   fAccess     The access permissions (IOMMU_IO_PERM_XXX). This is the
     4210 *                      permissions for the access being made.
    42044211 * @param   enmOp       The IOMMU operation being performed.
    4205  * @param   pIotlbe     The IOTLBE to update. Only updated when VINF_SUCCESS is
    4206  *                      returned, see remarks.
    4207  *
    4208  * @remarks Only the translated address and permission bits are updated in @a
    4209  *          pIotlbe when this function returns VINF_SUCCESS. Caller is expected to
    4210  *          have updated any other the fields already.
     4212 * @param   pGCPhysSpa  Where to store the translated system physical address. Only
     4213 *                      valid when translation succeeds and VINF_SUCCESS is
     4214 *                      returned!
    42114215 *
    42124216 * @thread  Any.
    42134217 */
    4214 static int iommuAmdLookupDeviceTables(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, size_t cbAccess, IOMMUOP enmOp,
    4215                                       PIOTLBE_T pIotlbe)
    4216 {
    4217     Assert(pIotlbe->uMagic == IOMMU_IOTLBE_MAGIC);
     4218static int iommuAmdLookupDeviceTables(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, size_t cbAccess, uint8_t fAccess,
     4219                                      IOMMUOP enmOp, PRTGCPHYS pGCPhysSpa)
     4220{
    42184221    PIOMMU pThis = PDMDEVINS_2_DATA(pDevIns, PIOMMU);
    42194222
     
    42284231        else
    42294232        {
    4230             iommuAmdUpdateIotlbe(uIova, 0 /* cShift */, IOMMU_IO_PERM_READ_WRITE, pIotlbe);
     4233            *pGCPhysSpa = uIova;
    42314234            return VINF_SUCCESS;
    42324235        }
     
    42534256        else
    42544257        {
    4255             iommuAmdUpdateIotlbe(uIova, 0 /* cShift */, IOMMU_IO_PERM_READ_WRITE, pIotlbe);
     4258            *pGCPhysSpa = uIova;
    42564259            return VINF_SUCCESS;
    42574260        }
    42584261
    4259         /* Walk the I/O page tables to translate and get permission bits for the IOVA access. */
    4260         rc = iommuAmdWalkIoPageTables(pDevIns, uDevId, uIova, cbAccess, IOMMU_IO_PERM_READ, &Dte, enmOp, pIotlbe);
    4261         if (RT_FAILURE(rc))
    4262             Log((IOMMU_LOG_PFX ": I/O page table walk failed. rc=%Rrc\n"));
     4262        /** @todo IOMMU: Perhaps do the <= 4K access case first, if the generic loop
     4263         *        below gets too expensive and when we have iommuAmdWalkIoPageDirectory. */
     4264
     4265        IOTLBE_T Iotlbe;
     4266        iommuAmdInitIotlbe(NIL_RTGCPHYS, 0 /* cShift */, IOMMU_IO_PERM_NONE, &Iotlbe);
     4267
     4268        uint64_t cbChecked = 0;
     4269        uint64_t uBaseIova = uIova & X86_PAGE_4K_BASE_MASK;
     4270        for (;;)
     4271        {
     4272            /* Walk the I/O page tables to translate and get permission bits for the IOVA access. */
     4273            rc = iommuAmdWalkIoPageTables(pDevIns, uDevId, uBaseIova, fAccess, &Dte, enmOp, &Iotlbe);
     4274            if (RT_SUCCESS(rc))
     4275            {
     4276                /* Record the translated base address (before continuing to check permission bits of any subsequent pages). */
     4277                if (cbChecked == 0)
     4278                    *pGCPhysSpa = Iotlbe.GCPhysSpa;
     4279
     4280                /** @todo IOMMU: Split large pages into 4K IOTLB entries and add to IOTLB cache. */
     4281
     4282                uint64_t const cbPhysPage = UINT64_C(1) << Iotlbe.cShift;
     4283                cbChecked += cbPhysPage;
     4284                if (cbAccess <= cbChecked)
     4285                    break;
     4286                uBaseIova += cbPhysPage;
     4287            }
     4288            else
     4289            {
     4290                Log((IOMMU_LOG_PFX ": I/O page table walk failed. uIova=%#RX64 uBaseIova=%#RX64 fAccess=%u rc=%Rrc\n",
     4291                     uIova, uBaseIova, fAccess, rc));
     4292                *pGCPhysSpa = NIL_RTGCPHYS;
     4293                return rc;
     4294            }
     4295        }
    42634296
    42644297        return rc;
     
    42944327    if (Ctrl.n.u1IommuEn)
    42954328    {
    4296         IOTLBE_T Iotlbe;
    4297         iommuAmdInitIotlbe(NIL_RTGCPHYS, 0 /* cShift */, IOMMU_IO_PERM_NONE, &Iotlbe);
    4298 
    42994329        /** @todo IOMMU: IOTLB cache lookup. */
    43004330
    43014331        /* Lookup the IOVA from the device tables. */
    4302         int rc = iommuAmdLookupDeviceTables(pDevIns, uDevId, uIova, cbRead, IOMMUOP_MEM_READ, &Iotlbe);
    4303 
    4304         /** @todo IOMMU: Cache translation. */
    4305 
    4306         *pGCPhysSpa = Iotlbe.GCPhysSpa;
    4307         return rc;
     4332        return iommuAmdLookupDeviceTables(pDevIns, uDevId, uIova, cbRead, IOMMU_IO_PERM_READ, IOMMUOP_MEM_READ, pGCPhysSpa);
    43084333    }
    43094334
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette