VirtualBox

Changeset 84515 in vbox for trunk/src


Ignore:
Timestamp:
May 25, 2020 4:06:07 PM (5 years ago)
Author:
vboxsync
Message:

AMD IOMMU: bugref:9654 MSI Interrupt remapping bits.

File:
1 edited

Legend:

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

    r84432 r84515  
    441441#define SYSMGTTYPE_MSG_INT_ALLOW                    (2)
    442442#define SYSMGTTYPE_DMA_ALLOW                        (3)
     443/** @} */
     444
     445/** @name IOMMU_INTR_CTRL_XX: DTE::IntCtl field values.
     446 * These are control bits for handling fixed and arbitrated interrupts.
     447 * In accordance with the AMD spec.
     448 * @{ */
     449#define IOMMU_INTR_CTRL_TARGET_ABORT                (0)
     450#define IOMMU_INTR_CTRL_FWD_UNMAPPED                (1)
     451#define IOMMU_INTR_CTRL_REMAP                       (2)
     452#define IOMMU_INTR_CTRL_RSVD                        (3)
    443453/** @} */
    444454
     
    17711781 *  really support a 64-bit MSI address? */
    17721782#define IOMMU_MSI_ADDR_VALID_MASK           UINT64_C(0xfffffffffffffffc)
     1783#define IOMMU_MSI_ADDR_ADDR_MASK            UINT64_C(0x00000000fff00000)
    17731784/** Pointer to an MSI address register. */
    17741785typedef MSI_ADDR_T *PMSI_ADDR_T;
     
    45634574
    45644575/**
     4576 * Reads an interrupt remapping table entry from guest memory given its DTE.
     4577 *
     4578 * @returns VBox status code.
     4579 * @param   pDevIns     The IOMMU device instance.
     4580 * @param   pDte        The device table entry.
     4581 * @param   enmOp       The IOMMU operation being performed.
     4582 * @param   pIrte       Where to store the interrupt remapping table entry.
     4583 *
     4584 * @thread  Any.
     4585 */
     4586static 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.
     4595 *
     4596 * @returns VBox status code.
     4597 * @param   pDevIns     The IOMMU instance data.
     4598 * @param   GCPhysIn    The source MSI address.
     4599 * @param   uDataIn     The source MSI data.
     4600 * @param   enmOp       The IOMMU operation being performed.
     4601 * @param   pGCPhysOut  Where to store the remapped MSI address.
     4602 * @param   puDataOut   Where to store the remapped MSI data.
     4603 *
     4604 * @thread  Any.
     4605 */
     4606static 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;
     4611}
     4612
     4613
     4614/**
     4615 * Looks up an MSI interrupt from the interrupt remapping table.
     4616 *
     4617 * @returns VBox status code.
     4618 * @param   pDevIns     The IOMMU instance data.
     4619 * @param   uDevId      The device ID.
     4620 * @param   GCPhysIn    The source MSI address.
     4621 * @param   uDataIn     The source MSI data.
     4622 * @param   enmOp       The IOMMU operation being performed.
     4623 * @param   pGCPhysOut  Where to store the remapped MSI address.
     4624 * @param   puDataOut   Where to store the remapped MSI data.
     4625 *
     4626 * @thread  Any.
     4627 */
     4628static int iommuAmdLookupIntrTable(PPDMDEVINS pDevIns, uint16_t uDevId, RTGCPHYS GCPhysIn, uint32_t uDataIn, IOMMUOP enmOp,
     4629                                   PRTGCPHYS pGCPhysOut, uint32_t *puDataOut)
     4630{
     4631    /* Read the device table entry from memory. */
     4632    DTE_T Dte;
     4633    int rc = iommuAmdReadDte(pDevIns, uDevId, enmOp, &Dte);
     4634    if (RT_SUCCESS(rc))
     4635    {
     4636        /* If the DTE is not valid, all interrupts are forwarded without remapping. */
     4637        if (Dte.n.u1IntrMapValid)
     4638        {
     4639            /* Validate bits 255:128 of the device table entry when DTE.IV is 1. */
     4640            uint64_t const fRsvd0 = Dte.au64[2] & ~IOMMU_DTE_QWORD_2_VALID_MASK;
     4641            uint64_t const fRsvd1 = Dte.au64[3] & ~IOMMU_DTE_QWORD_3_VALID_MASK;
     4642            if (RT_LIKELY(   !fRsvd0
     4643                          && !fRsvd1))
     4644            { /* likely */ }
     4645            else
     4646            {
     4647                Log((IOMMU_LOG_PFX ": Invalid reserved bits in DTE (u64[2]=%#RX64 u64[3]=%#RX64) -> Illegal DTE\n", fRsvd0,
     4648                     fRsvd1));
     4649                EVT_ILLEGAL_DTE_T Event;
     4650                iommuAmdInitIllegalDteEvent(uDevId, GCPhysIn, true /* fRsvdNotZero */, enmOp, &Event);
     4651                iommuAmdRaiseIllegalDteEvent(pDevIns, enmOp, &Event, kIllegalDteType_RsvdNotZero);
     4652                return VERR_IOMMU_INTR_REMAP_FAILED;
     4653            }
     4654
     4655            /** @todo IOMMU: Figure out how we'll redirect LINT0 and LINT1 legacy PIC
     4656             *        interrupts here. */
     4657
     4658            /*
     4659             * Validate the MSI source address.
     4660             *
     4661             * 64-bit MSIs are supported by the PCI and AMD IOMMU spec. However as far as the
     4662             * CPU is concerned, the MSI region is fixed and we must ensure no other device
     4663             * claims the region as I/O space.
     4664             *
     4665             * See PCI spec. 6.1.4. "Message Signaled Interrupt (MSI) Support".
     4666             * See AMD IOMMU spec. 2.8 "IOMMU Interrupt Support".
     4667             * See Intel spec. 10.11.1 "Message Address Register Format".
     4668             */
     4669            MSI_ADDR_T MsiAddrIn;
     4670            MsiAddrIn.u64 = GCPhysIn;
     4671            if ((MsiAddrIn.u64 & IOMMU_MSI_ADDR_ADDR_MASK) == VBOX_MSI_ADDR_BASE)
     4672            {
     4673                MSI_DATA_T MsiDataIn;
     4674                MsiDataIn.u32 = uDataIn;
     4675
     4676                /*
     4677                 * The IOMMU remaps fixed and arbitrated interrupts using the IRTE.
     4678                 * See AMD IOMMU spec. "2.2.5.1 Interrupt Remapping Tables, Guest Virtual APIC Not Enabled".
     4679                 */
     4680                uint8_t const u8DeliveryMode = MsiDataIn.n.u3DeliveryMode;
     4681                bool fPassThru = false;
     4682                switch (u8DeliveryMode)
     4683                {
     4684                    case VBOX_MSI_DELIVERY_MODE_FIXED:
     4685                    case VBOX_MSI_DELIVERY_MODE_LOWEST_PRIO:
     4686                    {
     4687                        uint8_t const uIntrCtrl = Dte.n.u2IntrCtrl;
     4688                        if (uIntrCtrl == IOMMU_INTR_CTRL_TARGET_ABORT)
     4689                        {
     4690                            Log((IOMMU_LOG_PFX ": IntCtl=0: Target aborting fixed/arbitrated interrupt -> Target abort\n"));
     4691                            iommuAmdSetPciTargetAbort(pDevIns);
     4692                            return VINF_SUCCESS;
     4693                        }
     4694
     4695                        if (uIntrCtrl == IOMMU_INTR_CTRL_FWD_UNMAPPED)
     4696                        {
     4697                            fPassThru = true;
     4698                            break;
     4699                        }
     4700
     4701                        if (uIntrCtrl == IOMMU_INTR_CTRL_REMAP)
     4702                        {
     4703                            /*
     4704                             * We don't support guest interrupt remapping yet. When we do, we'll need to
     4705                             * check Ctrl.u1GstVirtApicEn and use the guest Virtual APIC Table Root Pointer
     4706                             * in the DTE rather than the Interrupt Root Table Pointer. Since the caller
     4707                             * already reads the control register, add that as a parameter when we eventually
     4708                             * support guest interrupt remapping. For now, just assert.
     4709                             */
     4710                            PIOMMU pThis = PDMDEVINS_2_DATA(pDevIns, PIOMMU);
     4711                            Assert(!pThis->ExtFeat.n.u1GstVirtApicSup);
     4712                            NOREF(pThis);
     4713
     4714                            return iommuAmdRemapIntr(pDevIns, GCPhysIn, uDataIn, enmOp, pGCPhysOut, puDataOut);
     4715                        }
     4716
     4717                        /* Paranoia. */
     4718                        Assert(uIntrCtrl == IOMMU_INTR_CTRL_RSVD);
     4719
     4720                        Log((IOMMU_LOG_PFX ":IntCtl mode invalid %#x -> Illegal DTE", uIntrCtrl));
     4721                        EVT_ILLEGAL_DTE_T Event;
     4722                        iommuAmdInitIllegalDteEvent(uDevId, GCPhysIn, true /* fRsvdNotZero */, enmOp, &Event);
     4723                        iommuAmdRaiseIllegalDteEvent(pDevIns, enmOp, &Event, kIllegalDteType_RsvdIntCtl);
     4724                        return VERR_IOMMU_INTR_REMAP_FAILED;
     4725                    }
     4726
     4727                    /* SMIs are passed through unmapped. We don't implement SMI filters. */
     4728                    case VBOX_MSI_DELIVERY_MODE_SMI:        fPassThru = true;                   break;
     4729                    case VBOX_MSI_DELIVERY_MODE_NMI:        fPassThru = Dte.n.u1NmiPassthru;    break;
     4730                    case VBOX_MSI_DELIVERY_MODE_INIT:       fPassThru = Dte.n.u1InitPassthru;   break;
     4731                    case VBOX_MSI_DELIVERY_MODE_EXT_INT:    fPassThru = Dte.n.u1ExtIntPassthru; break;
     4732                    default:
     4733                    {
     4734                        Log((IOMMU_LOG_PFX ":MSI data delivery mode invalid %#x -> Target abort", u8DeliveryMode));
     4735                        iommuAmdSetPciTargetAbort(pDevIns);
     4736                        return VERR_IOMMU_INTR_REMAP_FAILED;
     4737                    }
     4738                }
     4739
     4740                if (fPassThru)
     4741                {
     4742                    *pGCPhysOut = GCPhysIn;
     4743                    *puDataOut  = uDataIn;
     4744                    return VINF_SUCCESS;
     4745                }
     4746
     4747                iommuAmdSetPciTargetAbort(pDevIns);
     4748                return VERR_IOMMU_INTR_REMAP_FAILED;
     4749            }
     4750            else
     4751            {
     4752                Log((IOMMU_LOG_PFX ":MSI address region invalid %#RX64.", MsiAddrIn.u64));
     4753                return VERR_IOMMU_INTR_REMAP_FAILED;
     4754            }
     4755        }
     4756        else
     4757        {
     4758            /** @todo IOMMU: Add to interrupt remapping cache. */
     4759            *pGCPhysOut = GCPhysIn;
     4760            *puDataOut  = uDataIn;
     4761            return VINF_SUCCESS;
     4762        }
     4763    }
     4764
     4765    Log((IOMMU_LOG_PFX ": Failed to read device table entry. uDevId=%#x rc=%Rrc\n", uDevId, rc));
     4766    return VERR_IOMMU_INTR_REMAP_FAILED;
     4767}
     4768
     4769
     4770/**
    45654771 * Interrupt remap request from a device.
    45664772 *
     
    45904796    if (Ctrl.n.u1IommuEn)
    45914797    {
    4592         /** @todo IOMMU: iommuAmdLookupIntrTable. */
     4798        /** @todo Cache? */
     4799
     4800        return iommuAmdLookupIntrTable(pDevIns, uDevId, GCPhysIn, uDataIn, IOMMUOP_INTR_REQ, pGCPhysOut, puDataOut);
    45934801    }
    45944802
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