VirtualBox

Changeset 87482 in vbox


Ignore:
Timestamp:
Jan 29, 2021 4:43:09 PM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
142504
Message:

AMD IOMMU: bugref:9654 Optimize address translation a tad bit. Avoid re-checking DTE level bits when the transfer spans multiple pages.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/err.h

    r87477 r87482  
    30413041/** Address translation failed. */
    30423042#define VERR_IOMMU_ADDR_TRANSLATION_FAILED          (-7207)
     3043/** Address translation disabled (but permission bits apply). */
     3044#define VINF_IOMMU_ADDR_TRANSLATION_DISABLED        7208
    30433045/** Access denied for the address. */
    3044 #define VERR_IOMMU_ADDR_ACCESS_DENIED               (-7208)
     3046#define VERR_IOMMU_ADDR_ACCESS_DENIED               (-7209)
    30453047/** Remapping failed for the interrupt. */
    3046 #define VERR_IOMMU_INTR_REMAP_FAILED                (-7209)
     3048#define VERR_IOMMU_INTR_REMAP_FAILED                (-7210)
    30473049/** Remapping denied for the interrupt (might have caused a PCI target abort). */
    3048 #define VERR_IOMMU_INTR_REMAP_DENIED                (-7210)
     3050#define VERR_IOMMU_INTR_REMAP_DENIED                (-7211)
    30493051/** Command not supported. */
    3050 #define VERR_IOMMU_CMD_NOT_SUPPORTED                (-7211)
     3052#define VERR_IOMMU_CMD_NOT_SUPPORTED                (-7212)
    30513053/** Command format (or reserved bits) invalid. */
    3052 #define VERR_IOMMU_CMD_INVALID_FORMAT               (-7212)
     3054#define VERR_IOMMU_CMD_INVALID_FORMAT               (-7213)
    30533055/** Command hardware failure. */
    3054 #define VERR_IOMMU_CMD_HW_ERROR                     (-7213)
     3056#define VERR_IOMMU_CMD_HW_ERROR                     (-7214)
    30553057/** IOMMU device is not present. */
    3056 #define VERR_IOMMU_NOT_PRESENT                      (-7214)
     3058#define VERR_IOMMU_NOT_PRESENT                      (-7215)
    30573059/** @} */
    30583060
  • trunk/src/VBox/Devices/Bus/DevIommuAmd.cpp

    r87470 r87482  
    26312631
    26322632/**
    2633  * Walks the I/O page table to translate the I/O virtual address to a system
    2634  * physical address.
     2633 * Performs pre-translation checks for the given device table entry.
    26352634 *
    26362635 * @returns VBox status code.
    26372636 * @param   pDevIns         The IOMMU device instance.
    2638  * @param   uIova           The I/O virtual address to translate. Must be 4K aligned.
     2637 * @param   uIova           The I/O virtual address to translate.
    26392638 * @param   uDevId          The device ID.
    26402639 * @param   fAccess         The access permissions (IOMMU_IO_PERM_XXX). This is the
    26412640 *                          permissions for the access being made.
    26422641 * @param   pDte            The device table entry.
     2642 * @param   fRootPage       Whether to check the root of the access (required only
     2643 *                          for the first page of an access).
    26432644 * @param   enmOp           The IOMMU operation being performed.
    26442645 * @param   pWalkResult     Where to store the results of the I/O page walk. This is
     
    26472648 * @thread  Any.
    26482649 */
    2649 static int iommuAmdIoPageTableWalk(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, uint8_t fAccess, PCDTE_T pDte,
    2650                                    IOMMUOP enmOp, PIOWALKRESULT pWalkResult)
    2651 {
    2652     Assert(pDte->n.u1Valid);
    2653     Assert(!(uIova & X86_PAGE_4K_OFFSET_MASK));
    2654 
    2655     /* If the translation is not valid, raise an I/O page fault. */
     2650static int iommuAmdPreTranslateChecks(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, uint8_t fAccess, PCDTE_T pDte,
     2651                                      IOMMUOP enmOp, PIOWALKRESULT pWalkResult)
     2652{
     2653    /*
     2654     * Check if the translation is valid, otherwise raise an I/O page fault.
     2655     */
    26562656    if (pDte->n.u1TranslationValid)
    26572657    { /* likely */ }
     
    26722672    }
    26732673
    2674     /* If the root page table level is 0, translation is skipped and access is controlled by the permission bits. */
     2674    /*
     2675     * Check permissions bits in the DTE.
     2676     * Note: This MUST be checked prior to checking the root page table level below!
     2677     */
     2678    uint8_t const fDtePerm  = (pDte->au64[0] >> IOMMU_IO_PERM_SHIFT) & IOMMU_IO_PERM_MASK;
     2679    if ((fAccess & fDtePerm) == fAccess)
     2680    { /* likely */ }
     2681    else
     2682    {
     2683        LogFunc(("Permission denied by DTE (fAccess=%#x fDtePerm=%#x) -> IOPF\n", fAccess, fDtePerm));
     2684        EVT_IO_PAGE_FAULT_T EvtIoPageFault;
     2685        iommuAmdIoPageFaultEventInit(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */,
     2686                                     true /* fPermDenied */, enmOp, &EvtIoPageFault);
     2687        iommuAmdIoPageFaultEventRaise(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault, kIoPageFaultType_PermDenied);
     2688        return VERR_IOMMU_ADDR_ACCESS_DENIED;
     2689    }
     2690
     2691    /*
     2692     * If the root page table level is 0, translation is disabled and GPA=SPA and
     2693     * the DTE.IR and DTE.IW bits control permissions (verified above).
     2694     */
    26752695    uint8_t const uMaxLevel = pDte->n.u3Mode;
    26762696    if (uMaxLevel != 0)
     
    26782698    else
    26792699    {
    2680         uint8_t const fDtePerm = (pDte->au64[0] >> IOMMU_IO_PERM_SHIFT) & IOMMU_IO_PERM_MASK;
    2681         if ((fAccess & fDtePerm) != fAccess)
    2682         {
    2683             LogFunc(("Access denied for IOVA %#RX64. uDevId=%#x fAccess=%#x fDtePerm=%#x\n", uIova, uDevId, fAccess, fDtePerm));
    2684             return VERR_IOMMU_ADDR_ACCESS_DENIED;
    2685         }
     2700        Assert((fAccess & fDtePerm) == fAccess);    /* Verify we've checked permissions. */
    26862701        pWalkResult->GCPhysSpa = uIova;
    26872702        pWalkResult->cShift    = 0;
    26882703        pWalkResult->fIoPerm   = fDtePerm;
    2689         return VINF_SUCCESS;
    2690     }
    2691 
    2692     /* If the root page table level exceeds the allowed host-address translation level, page walk is terminated. */
     2704        return VINF_IOMMU_ADDR_TRANSLATION_DISABLED;
     2705    }
     2706
     2707    /*
     2708     * If the root page table level exceeds the allowed host-address translation level,
     2709     * page walk is terminated and translation fails.
     2710     */
    26932711    if (uMaxLevel <= IOMMU_MAX_HOST_PT_LEVEL)
    26942712    { /* likely */ }
     
    27072725    }
    27082726
    2709     /* Check permissions bits of the root page table. */
    2710     uint8_t const fRootPtePerm  = (pDte->au64[0] >> IOMMU_IO_PERM_SHIFT) & IOMMU_IO_PERM_MASK;
    2711     if ((fAccess & fRootPtePerm) == fAccess)
    2712     { /* likely */ }
    2713     else
    2714     {
    2715         LogFunc(("Permission denied (fAccess=%#x fRootPtePerm=%#x) -> IOPF\n", fAccess, fRootPtePerm));
    2716         EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    2717         iommuAmdIoPageFaultEventInit(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */,
    2718                                      true /* fPermDenied */, enmOp, &EvtIoPageFault);
    2719         iommuAmdIoPageFaultEventRaise(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault, kIoPageFaultType_PermDenied);
    2720         return VERR_IOMMU_ADDR_TRANSLATION_FAILED;
    2721     }
    2722 
    2723     /** @todo r=ramshankar: IOMMU: Consider splitting the rest of this into a separate
    2724      *        function called iommuAmdWalkIoPageDirectory() and call it for multi-page
    2725      *        accesses from the 2nd page. We can avoid re-checking the DTE root-page
    2726      *        table entry every time. Not sure if it's worth optimizing that case now
    2727      *        or if at all. */
     2727    /* The DTE allows translations for this device. */
     2728    return VINF_SUCCESS;
     2729}
     2730
     2731
     2732/**
     2733 * Walks the I/O page table to translate the I/O virtual address to a system
     2734 * physical address.
     2735 *
     2736 * @returns VBox status code.
     2737 * @param   pDevIns         The IOMMU device instance.
     2738 * @param   uIova           The I/O virtual address to translate. Must be 4K aligned.
     2739 * @param   uDevId          The device ID.
     2740 * @param   fAccess         The access permissions (IOMMU_IO_PERM_XXX). This is the
     2741 *                          permissions for the access being made.
     2742 * @param   pDte            The device table entry.
     2743 * @param   enmOp           The IOMMU operation being performed.
     2744 * @param   pWalkResult     Where to store the results of the I/O page walk. This is
     2745 *                          only updated when VINF_SUCCESS is returned.
     2746 *
     2747 * @thread  Any.
     2748 */
     2749static int iommuAmdIoPageTableWalk(PPDMDEVINS pDevIns, uint16_t uDevId, uint64_t uIova, uint8_t fAccess, PCDTE_T pDte,
     2750                                   IOMMUOP enmOp, PIOWALKRESULT pWalkResult)
     2751{
     2752    Assert(pDte->n.u1Valid);
     2753    Assert(!(uIova & X86_PAGE_4K_OFFSET_MASK));
    27282754
    27292755    /* The virtual address bits indexing table. */
     
    29262952            {
    29272953                /* If the IOVA is subject to address exclusion, addresses are forwarded without translation. */
    2928                 if (   !pThis->ExclRangeBaseAddr.n.u1ExclEnable         /** @todo lock or make atomic read? */
     2954                if (   !pThis->ExclRangeBaseAddr.n.u1ExclEnable         /** @todo lock or make atomic read! */
    29292955                    || !iommuAmdIsDvaInExclRange(pThis, &Dte, uIova))
    29302956                {
    2931                     /* Walk the I/O page tables to translate the IOVA and check permission for each page in the access. */
    2932                     size_t   cbRemaining = cbAccess;
    2933                     uint64_t uIovaPage   = uIova & X86_PAGE_4K_BASE_MASK;
    2934                     uint64_t offIova     = uIova & X86_PAGE_4K_OFFSET_MASK;
    2935                     uint64_t cbPages     = 0;
    2936                     for (;;)
     2957                    IOWALKRESULT WalkResult;
     2958                    RT_ZERO(WalkResult);
     2959                    rc = iommuAmdPreTranslateChecks(pDevIns, uDevId, uIova, fAccess, &Dte, enmOp, &WalkResult);
     2960                    if (rc == VINF_SUCCESS)
    29372961                    {
    2938                         IOWALKRESULT WalkResult;
    2939                         RT_ZERO(WalkResult);
    2940                         rc = iommuAmdIoPageTableWalk(pDevIns, uDevId, uIovaPage, fAccess, &Dte, enmOp, &WalkResult);
    2941                         if (RT_SUCCESS(rc))
     2962                        /* Walk the I/O page tables to translate the IOVA and check permissions for the
     2963                           remaining pages in the access. */
     2964                        size_t   cbRemaining = cbAccess;
     2965                        uint64_t uIovaPage   = uIova & X86_PAGE_4K_BASE_MASK;
     2966                        uint64_t offIova     = uIova & X86_PAGE_4K_OFFSET_MASK;
     2967                        uint64_t cbPages     = 0;
     2968                        for (;;)
    29422969                        {
    2943                             /* If translation is disabled for this device (root paging mode is 0), we're done. */
    2944                             if (WalkResult.cShift == 0)
     2970                            rc = iommuAmdIoPageTableWalk(pDevIns, uDevId, uIovaPage, fAccess, &Dte, enmOp, &WalkResult);
     2971                            if (RT_SUCCESS(rc))
    29452972                            {
    2946                                 GCPhysSpa   = uIova;
    2947                                 cbRemaining = 0;
    2948                                 break;
    2949                             }
    2950 
    2951                             /* Store the translated address before continuing to access more pages. */
    2952                             Assert(WalkResult.cShift >= X86_PAGE_4K_SHIFT);
    2953                             if (cbRemaining == cbAccess)
    2954                             {
    2955                                 uint64_t const offMask = ~(UINT64_C(0xffffffffffffffff) << WalkResult.cShift);
    2956                                 uint64_t const offSpa  = uIova & offMask;
    2957                                 GCPhysSpa = WalkResult.GCPhysSpa | offSpa;
    2958                             }
    2959                             /* Check if addresses translated so far are physically contiguous. */
    2960                             else if ((GCPhysSpa & X86_PAGE_4K_BASE_MASK) + cbPages == WalkResult.GCPhysSpa)
    2961                             { /* likely */ }
    2962                             else
    2963                                 break;
    2964 
    2965                             /* Check if we need to access more pages. */
    2966                             uint64_t const cbPage = UINT64_C(1) << WalkResult.cShift;
    2967                             if (cbRemaining > cbPage - offIova)
    2968                             {
    2969                                 cbRemaining -= (cbPage - offIova);  /* Calculate how much more we need to access. */
    2970                                 cbPages     += cbPage;              /* Update size of all pages read thus far. */
    2971                                 uIovaPage   += cbPage;              /* Update address of the next access. */
    2972                                 offIova      = 0;                   /* After the first page, all pages are accessed from off 0. */
     2973                                /* Store the translated address before continuing to access more pages. */
     2974                                Assert(WalkResult.cShift >= X86_PAGE_4K_SHIFT);
     2975                                if (cbRemaining == cbAccess)
     2976                                {
     2977                                    uint64_t const offMask = ~(UINT64_C(0xffffffffffffffff) << WalkResult.cShift);
     2978                                    uint64_t const offSpa  = uIova & offMask;
     2979                                    GCPhysSpa = WalkResult.GCPhysSpa | offSpa;
     2980                                }
     2981                                /* Check if addresses translated so far result in a physically contiguous region. */
     2982                                else if ((GCPhysSpa & X86_PAGE_4K_BASE_MASK) + cbPages == WalkResult.GCPhysSpa)
     2983                                { /* likely */ }
     2984                                else
     2985                                    break;
     2986
     2987                                /* Check if we need to access more pages. */
     2988                                uint64_t const cbPage = UINT64_C(1) << WalkResult.cShift;
     2989                                if (cbRemaining > cbPage - offIova)
     2990                                {
     2991                                    cbRemaining -= (cbPage - offIova);  /* Calculate how much more we need to access. */
     2992                                    cbPages     += cbPage;              /* Update size of all pages read thus far. */
     2993                                    uIovaPage   += cbPage;              /* Update address of the next access. */
     2994                                    offIova      = 0;                   /* After first page, all pages are accessed from off 0. */
     2995                                }
     2996                                else
     2997                                {
     2998                                    cbRemaining = 0;
     2999                                    break;
     3000                                }
    29733001                            }
    29743002                            else
    29753003                            {
    2976                                 cbRemaining = 0;
     3004                                /* Translation failed. */
     3005                                GCPhysSpa   = NIL_RTGCPHYS;
     3006                                cbRemaining = cbAccess;
    29773007                                break;
    29783008                            }
    29793009                        }
    2980                         else
    2981                         {
    2982                             GCPhysSpa   = NIL_RTGCPHYS;
    2983                             cbRemaining = cbAccess;
    2984                             break;
    2985                         }
     3010
     3011                        /* Update how much contiguous memory was accessed. */
     3012                        cbContiguous = cbAccess - cbRemaining;
    29863013                    }
    2987 
    2988                     /* Calculate how much contiguous memory was accessed. */
    2989                     cbContiguous = cbAccess - cbRemaining;
     3014                    else if (rc == VINF_IOMMU_ADDR_TRANSLATION_DISABLED)
     3015                    {
     3016                        /* Translation is disabled for this device (root paging mode is 0). */
     3017                        GCPhysSpa    =  uIova;
     3018                        cbContiguous = cbAccess;
     3019                        rc = VINF_SUCCESS;
     3020
     3021                        /* Paranoia. */
     3022                        Assert(WalkResult.cShift   == 0);
     3023                        Assert(WalkResult.GCPhysSpa == uIova);
     3024                        Assert((WalkResult.fIoPerm & fAccess) == fAccess);
     3025                        /** @todo IOMMU: Add to IOLTB cache. */
     3026                    }
     3027                    else
     3028                    {
     3029                        /* Translation failed or access is denied. */
     3030                        GCPhysSpa    = NIL_RTGCPHYS;
     3031                        cbContiguous = 0;
     3032                        Assert(RT_FAILURE(rc));
     3033                    }
    29903034                }
    29913035                else
Note: See TracChangeset for help on using the changeset viewer.

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