VirtualBox

Ignore:
Timestamp:
May 26, 2020 9:38:30 AM (5 years ago)
Author:
vboxsync
Message:

AMD IOMMU: bugref:9654 Interrupt remapping bits.

File:
1 edited

Legend:

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

    r84515 r84530  
    669669#define IOMMU_DTE_QWORD_3_VALID_MASK            UINT64_C(0xffc0000000000000)
    670670
     671/* Mask of the interrupt table root pointer. */
     672#define IOMMU_DTE_IRTE_ROOT_PTR_MASK            UINT64_C(0x000fffffffffff80)
     673
    671674/**
    672675 * I/O Page Translation Entry.
     
    775778} IRTE_T;
    776779AssertCompileSize(IRTE_T, 4);
     780/** The number of bits to shift the IRTE offset to get the IRTE. */
     781#define IOMMU_IRTE_SIZE_SHIFT   (2)
    777782/** Pointer to an IRTE_T struct. */
    778783typedef IRTE_T *PIRTE_T;
     
    18101815} MSI_DATA_T;
    18111816AssertCompileSize(MSI_DATA_T, 4);
    1812 #define IOMMU_MSI_DATA_VALID_MASK       UINT64_C(0x000000000000ffff)
     1817#define IOMMU_MSI_DATA_VALID_MASK           UINT64_C(0x000000000000ffff)
     1818/** The IRTE offset corresponds directly to bits 10:0 of the originating MSI
     1819 *  interrupt message. See AMD IOMMU spec. 2.2.5 "Interrupt Remapping Tables". */
     1820#define IOMMU_MSI_DATA_IRTE_OFFSET_MASK     UINT32_C(0x000007ff)
     1821
    18131822/** Pointer to an MSI data register. */
    18141823typedef MSI_DATA_T *PMSI_DATA_T;
     
    21882197{
    21892198    kIllegalDteType_RsvdNotZero = 0,
    2190     kIllegalDteType_RsvdIntTab,
     2199    kIllegalDteType_RsvdIntTabLen,
    21912200    kIllegalDteType_RsvdIoCtl,
    21922201    kIllegalDteType_RsvdIntCtl
     
    41354144    }
    41364145
     4146
    41374147    return rc;
    41384148}
     
    41724182         *        the DTE) return the state computed so far and raises an I/O page fault. So
    41734183         *        returning an invalid translation rather than skipping translation. */
     4184        Log((IOMMU_LOG_PFX ": Translation valid bit not set -> IOPF"));
    41744185        EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    41754186        iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, false /* fPresent */, false /* fRsvdNotZero */,
     
    42064217         *        raising an ILLEGAL_DEV_TABLE_ENTRY event or an IO_PAGE_FAULT event here.
    42074218         *        I'm just going with I/O page fault. */
     4219        Log((IOMMU_LOG_PFX ": Invalid root page table level %#x -> IOPF", uMaxLevel));
    42084220        EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    42094221        iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */,
     
    42204232    else
    42214233    {
     4234        Log((IOMMU_LOG_PFX ": Permission denied (fAccess=%#x fPtePerm=%#x) -> IOPF", fAccess, fPtePerm));
    42224235        EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    42234236        iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */,
     
    42774290        else
    42784291        {
     4292            Log((IOMMU_LOG_PFX ": Page table entry not present -> IOPF", fAccess, fPtePerm));
    42794293            EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    42804294            iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, false /* fPresent */, false /* fRsvdNotZero */,
     
    42904304        else
    42914305        {
     4306            Log((IOMMU_LOG_PFX ": Page table entry permission denied (fAccess=%#x fPtePerm=%#x) -> IOPF", fAccess, fPtePerm));
    42924307            EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    42934308            iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */,
     
    43264341            }
    43274342
     4343            Log((IOMMU_LOG_PFX ": Page size invalid cShift=%#x -> IOPF", cShift));
    43284344            EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    43294345            iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */,
     
    43404356        else
    43414357        {
     4358            Log((IOMMU_LOG_PFX ": Next level of PDE invalid uNextLevel=%#x -> IOPF", uNextLevel));
    43424359            EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    43434360            iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */,
     
    43564373        else
    43574374        {
     4375            Log((IOMMU_LOG_PFX ": Next level (%#x) must be less than the current level (%#x) -> IOPF", uNextLevel, uLevel));
    43584376            EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    43594377            iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */,
     
    43734391        else
    43744392        {
     4393            Log((IOMMU_LOG_PFX ": IOVA of skipped levels are not zero %#RX64 (SkipMask=%#RX64) -> IOPF", uIova, uIovaSkipMask));
    43754394            EVT_IO_PAGE_FAULT_T EvtIoPageFault;
    43764395            iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, uIova, true /* fPresent */, false /* fRsvdNotZero */,
     
    45784597 * @returns VBox status code.
    45794598 * @param   pDevIns     The IOMMU device instance.
     4599 * @param   uDevId      The device ID.
    45804600 * @param   pDte        The device table entry.
     4601 * @param   GCPhysIn    The source MSI address.
     4602 * @param   uDataIn     The source MSI data.
    45814603 * @param   enmOp       The IOMMU operation being performed.
    45824604 * @param   pIrte       Where to store the interrupt remapping table entry.
     
    45844606 * @thread  Any.
    45854607 */
    4586 static int iommuAmdReadIrte(PPDMDEVINS pDevIns, PCDTE_T pDte, IOMMUOP enmOp, IRTE_T pIrte)
    4587 {
    4588     RT_NOREF(pDevIns, pDte, enmOp, pIrte);
    4589     return VERR_NOT_IMPLEMENTED;
    4590 }
    4591 
    4592 
    4593 /**
    4594  * Remap the interrupt from the appropriate IRTE.
     4608static int iommuAmdReadIrte(PPDMDEVINS pDevIns, uint16_t uDevId, PCDTE_T pDte, RTGCPHYS GCPhysIn, uint32_t uDataIn,
     4609                            IOMMUOP enmOp, PIRTE_T pIrte)
     4610{
     4611    RTGCPHYS const GCPhysIntrTable = pDte->au64[2] & IOMMU_DTE_IRTE_ROOT_PTR_MASK;
     4612    uint16_t const offIrte         = (uDataIn & IOMMU_MSI_DATA_IRTE_OFFSET_MASK) << IOMMU_IRTE_SIZE_SHIFT;
     4613    RTGCPHYS const GCPhysIrte      = GCPhysIntrTable + offIrte;
     4614
     4615    /* Ensure the IRTE offset is within the specified table size. */
     4616    Assert(pDte->n.u4IntrTableLength < 12);
     4617    if (offIrte < (1 << pDte->n.u4IntrTableLength) << IOMMU_IRTE_SIZE_SHIFT)
     4618    { /* likely */ }
     4619    else
     4620    {
     4621        EVT_IO_PAGE_FAULT_T EvtIoPageFault;
     4622        iommuAmdInitIoPageFaultEvent(uDevId, pDte->n.u16DomainId, GCPhysIn, false /* fPresent */, false /* fRsvdNotZero */,
     4623                                     false /* fPermDenied */, enmOp, &EvtIoPageFault);
     4624        iommuAmdRaiseIoPageFaultEvent(pDevIns, pDte, NULL /* pIrte */, enmOp, &EvtIoPageFault,
     4625                                      kIoPageFaultType_IrteAddrInvalid);
     4626        return VERR_IOMMU_ADDR_TRANSLATION_FAILED;
     4627    }
     4628
     4629    /* Read the IRTE from memory. */
     4630    Assert(!(GCPhysIrte & 3));
     4631    int rc = PDMDevHlpPCIPhysRead(pDevIns, GCPhysIrte, pIrte, sizeof(*pIrte));
     4632    if (RT_SUCCESS(rc))
     4633        return VINF_SUCCESS;
     4634
     4635    /** @todo r=ramshankar: The IOMMU spec. does not tell what kind of error is
     4636     *        reported in this situation. Is it an I/O page fault or a device table
     4637     *        hardware error? There's no interrupt table hardware error event, but
     4638     *        it's unclear what we should do here. */
     4639    Log((IOMMU_LOG_PFX ": Failed to read interrupt table entry at %#RGp. rc=%Rrc -> ???\n", GCPhysIrte, rc));
     4640    return VERR_IOMMU_IPE_4;
     4641}
     4642
     4643
     4644/**
     4645 * Remap the interrupt using the interrupt remapping table.
    45954646 *
    45964647 * @returns VBox status code.
    45974648 * @param   pDevIns     The IOMMU instance data.
     4649 * @param   uDevId      The device ID.
     4650 * @param   pDte        The device table entry.
    45984651 * @param   GCPhysIn    The source MSI address.
    45994652 * @param   uDataIn     The source MSI data.
     
    46044657 * @thread  Any.
    46054658 */
    4606 static int iommuAmdRemapIntr(PPDMDEVINS pDevIns, RTGCPHYS GCPhysIn, uint32_t uDataIn, IOMMUOP enmOp,
    4607                              PRTGCPHYS pGCPhysOut, uint32_t *puDataOut)
    4608 {
    4609     RT_NOREF(pDevIns, GCPhysIn, uDataIn, enmOp, pGCPhysOut, puDataOut);
    4610     return VERR_NOT_IMPLEMENTED;
     4659static int iommuAmdRemapIntr(PPDMDEVINS pDevIns, uint16_t uDevId, PCDTE_T pDte, RTGCPHYS GCPhysIn, uint32_t uDataIn,
     4660                             IOMMUOP enmOp, PRTGCPHYS pGCPhysOut, uint32_t *puDataOut)
     4661{
     4662    Assert(pDte->n.u2IntrCtrl == IOMMU_INTR_CTRL_REMAP);
     4663
     4664    IRTE_T Irte;
     4665    int rc = iommuAmdReadIrte(pDevIns, uDevId, pDte, GCPhysIn, uDataIn, enmOp, &Irte);
     4666    if (RT_SUCCESS(rc))
     4667    {
     4668        /** @todo Remap. */
     4669        return VERR_NOT_IMPLEMENTED;
     4670    }
     4671
     4672    RT_NOREF(pGCPhysOut, puDataOut);
     4673    return rc;
    46114674}
    46124675
     
    47014764                        if (uIntrCtrl == IOMMU_INTR_CTRL_REMAP)
    47024765                        {
     4766                            /* Validate the encoded interrupt table length when IntCtl specifies remapping. */
     4767                            uint32_t const uIntTabLen = Dte.n.u4IntrTableLength;
     4768                            if (Dte.n.u4IntrTableLength < 12)
     4769                            { /* likely */ }
     4770                            else
     4771                            {
     4772                                Log((IOMMU_LOG_PFX ": Invalid interrupt table length %#x -> Illegal DTE\n", uIntTabLen));
     4773                                EVT_ILLEGAL_DTE_T Event;
     4774                                iommuAmdInitIllegalDteEvent(uDevId, GCPhysIn, false /* fRsvdNotZero */, enmOp, &Event);
     4775                                iommuAmdRaiseIllegalDteEvent(pDevIns, enmOp, &Event, kIllegalDteType_RsvdIntTabLen);
     4776                                return VERR_IOMMU_INTR_REMAP_FAILED;
     4777                            }
     4778
    47034779                            /*
    47044780                             * We don't support guest interrupt remapping yet. When we do, we'll need to
     
    47124788                            NOREF(pThis);
    47134789
    4714                             return iommuAmdRemapIntr(pDevIns, GCPhysIn, uDataIn, enmOp, pGCPhysOut, puDataOut);
     4790                            return iommuAmdRemapIntr(pDevIns, uDevId, &Dte, GCPhysIn, uDataIn, enmOp, pGCPhysOut, puDataOut);
    47154791                        }
    47164792
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