Merge tag 'iommu-updates-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 May 2013 21:59:13 +0000 (14:59 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 May 2013 21:59:13 +0000 (14:59 -0700)
Pull IOMMU updates from Joerg Roedel:
 "The updates are mostly about the x86 IOMMUs this time.

  Exceptions are the groundwork for the PAMU IOMMU from Freescale (for a
  PPC platform) and an extension to the IOMMU group interface.

  On the x86 side this includes a workaround for VT-d to disable
  interrupt remapping on broken chipsets.  On the AMD-Vi side the most
  important new feature is a kernel command-line interface to override
  broken information in IVRS ACPI tables and get interrupt remapping
  working this way.

  Besides that there are small fixes all over the place."

* tag 'iommu-updates-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (24 commits)
  iommu/tegra: Fix printk formats for dma_addr_t
  iommu: Add a function to find an iommu group by id
  iommu/vt-d: Remove warning for HPET scope type
  iommu: Move swap_pci_ref function to drivers/iommu/pci.h.
  iommu/vt-d: Disable translation if already enabled
  iommu/amd: fix error return code in early_amd_iommu_init()
  iommu/AMD: Per-thread IOMMU Interrupt Handling
  iommu: Include linux/err.h
  iommu/amd: Workaround for ERBT1312
  iommu/amd: Document ivrs_ioapic and ivrs_hpet parameters
  iommu/amd: Don't report firmware bugs with cmd-line ivrs overrides
  iommu/amd: Add ioapic and hpet ivrs override
  iommu/amd: Add early maps for ioapic and hpet
  iommu/amd: Extend IVRS special device data structure
  iommu/amd: Move add_special_device() to __init
  iommu: Fix compile warnings with forward declarations
  iommu/amd: Properly initialize irq-table lock
  iommu/amd: Use AMD specific data structure for irq remapping
  iommu/amd: Remove map_sg_no_iommu()
  iommu/vt-d: add quirk for broken interrupt remapping on 55XX chipsets
  ...

1  2 
Documentation/kernel-parameters.txt
drivers/iommu/amd_iommu.c
drivers/iommu/amd_iommu_init.c
drivers/iommu/amd_iommu_types.h

index 8920f9f5fa9e6a319244abf366e96c2e14b97152,47bb23c22b1fccebde1f5013e1eba83400a462ce..c3bfacb92910dc8d044bec88122c4a85328ed8d5
@@@ -44,8 -44,6 +44,8 @@@ parameter is applicable
        AVR32   AVR32 architecture is enabled.
        AX25    Appropriate AX.25 support is enabled.
        BLACKFIN Blackfin architecture is enabled.
 +      CLK     Common clock infrastructure is enabled.
 +      CMA     Contiguous Memory Area support is enabled.
        DRM     Direct Rendering Management support is enabled.
        DYNAMIC_DEBUG Build in debug messages and enable them at runtime
        EDD     BIOS Enhanced Disk Drive Services (EDD) is enabled
@@@ -322,13 -320,6 +322,13 @@@ bytes respectively. Such letter suffixe
                        on: enable for both 32- and 64-bit processes
                        off: disable for both 32- and 64-bit processes
  
 +      alloc_snapshot  [FTRACE]
 +                      Allocate the ftrace snapshot buffer on boot up when the
 +                      main buffer is allocated. This is handy if debugging
 +                      and you need to use tracing_snapshot() on boot up, and
 +                      do not want to use tracing_snapshot_alloc() as it needs
 +                      to be done where GFP_KERNEL allocations are allowed.
 +
        amd_iommu=      [HW,X86-64]
                        Pass parameters to the AMD IOMMU driver in the system.
                        Possible values are:
  
        cio_ignore=     [S390]
                        See Documentation/s390/CommonIO for details.
 +      clk_ignore_unused
 +                      [CLK]
 +                      Keep all clocks already enabled by bootloader on,
 +                      even if no driver has claimed them. This is useful
 +                      for debug and development, but should not be
 +                      needed on a platform with proper driver support.
 +                      For more information, see Documentation/clk.txt.
  
        clock=          [BUGS=X86-32, HW] gettimeofday clocksource override.
                        [Deprecated]
                        (mmio) or 32-bit (mmio32).
                        The options are the same as for ttyS, above.
  
 -      earlyprintk=    [X86,SH,BLACKFIN]
 +      earlyprintk=    [X86,SH,BLACKFIN,ARM]
                        earlyprintk=vga
                        earlyprintk=xen
                        earlyprintk=serial[,ttySn[,baudrate]]
 +                      earlyprintk=serial[,0x...[,baudrate]]
                        earlyprintk=ttySn[,baudrate]
                        earlyprintk=dbgp[debugController#]
  
 +                      earlyprintk is useful when the kernel crashes before
 +                      the normal console is initialized. It is not enabled by
 +                      default because it has some cosmetic problems.
 +
                        Append ",keep" to not disable it when the real console
                        takes over.
  
                        Only vga or serial or usb debug port at a time.
  
 -                      Currently only ttyS0 and ttyS1 are supported.
 +                      Currently only ttyS0 and ttyS1 may be specified by
 +                      name.  Other I/O ports may be explicitly specified
 +                      on some architectures (x86 and arm at least) by
 +                      replacing ttySn with an I/O port address, like this:
 +                              earlyprintk=serial,0x1008,115200
 +                      You can find the port for a given device in
 +                      /proc/tty/driver/serial:
 +                              2: uart:ST16650V2 port:00001008 irq:18 ...
  
                        Interaction with the standard serial driver is not
                        very good.
  
        iucv=           [HW,NET]
  
+       ivrs_ioapic     [HW,X86_64]
+                       Provide an override to the IOAPIC-ID<->DEVICE-ID
+                       mapping provided in the IVRS ACPI table. For
+                       example, to map IOAPIC-ID decimal 10 to
+                       PCI device 00:14.0 write the parameter as:
+                               ivrs_ioapic[10]=00:14.0
+       ivrs_hpet       [HW,X86_64]
+                       Provide an override to the HPET-ID<->DEVICE-ID
+                       mapping provided in the IVRS ACPI table. For
+                       example, to map HPET-ID decimal 0 to
+                       PCI device 00:14.0 write the parameter as:
+                               ivrs_hpet[0]=00:14.0
        js=             [HW,JOY] Analog joystick
                        See Documentation/input/joystick.txt.
  
        module.sig_enforce
                        [KNL] When CONFIG_MODULE_SIG is set, this means that
                        modules without (valid) signatures will fail to load.
 -                      Note that if CONFIG_MODULE_SIG_ENFORCE is set, that
 +                      Note that if CONFIG_MODULE_SIG_FORCE is set, that
                        is always true, so this option does nothing.
  
        mousedev.tap_time=
                        Valid arguments: on, off
                        Default: on
  
 +      nohz_full=      [KNL,BOOT]
 +                      In kernels built with CONFIG_NO_HZ_FULL=y, set
 +                      the specified list of CPUs whose tick will be stopped
 +                      whenever possible. The boot CPU will be forced outside
 +                      the range to maintain the timekeeping.
 +                      The CPUs in this range must also be included in the
 +                      rcu_nocbs= set.
 +
        noiotrap        [SH] Disables trapped I/O port accesses.
  
        noirqdebug      [X86-32] Disables the code which attempts to detect and
        noreplace-smp   [X86-32,SMP] Don't replace SMP instructions
                        with UP alternatives
  
 -      noresidual      [PPC] Don't use residual data on PReP machines.
 -
        nordrand        [X86] Disable the direct use of the RDRAND
                        instruction even if it is supported by the
                        processor.  RDRAND is still available to user
                        In kernels built with CONFIG_RCU_NOCB_CPU=y, set
                        the specified list of CPUs to be no-callback CPUs.
                        Invocation of these CPUs' RCU callbacks will
 -                      be offloaded to "rcuoN" kthreads created for
 -                      that purpose.  This reduces OS jitter on the
 +                      be offloaded to "rcuox/N" kthreads created for
 +                      that purpose, where "x" is "b" for RCU-bh, "p"
 +                      for RCU-preempt, and "s" for RCU-sched, and "N"
 +                      is the CPU number.  This reduces OS jitter on the
                        offloaded CPUs, which can be useful for HPC and
 +
                        real-time workloads.  It can also improve energy
                        efficiency for asymmetric multiprocessors.
  
                        leaf rcu_node structure.  Useful for very large
                        systems.
  
 +      rcutree.jiffies_till_first_fqs= [KNL,BOOT]
 +                      Set delay from grace-period initialization to
 +                      first attempt to force quiescent states.
 +                      Units are jiffies, minimum value is zero,
 +                      and maximum value is HZ.
 +
 +      rcutree.jiffies_till_next_fqs= [KNL,BOOT]
 +                      Set delay between subsequent attempts to force
 +                      quiescent states.  Units are jiffies, minimum
 +                      value is one, and maximum value is HZ.
 +
        rcutree.qhimark=        [KNL,BOOT]
                        Set threshold of queued
                        RCU callbacks over which batch limiting is disabled.
        rcutree.rcu_cpu_stall_timeout= [KNL,BOOT]
                        Set timeout for RCU CPU stall warning messages.
  
 -      rcutree.jiffies_till_first_fqs= [KNL,BOOT]
 -                      Set delay from grace-period initialization to
 -                      first attempt to force quiescent states.
 -                      Units are jiffies, minimum value is zero,
 -                      and maximum value is HZ.
 +      rcutree.rcu_idle_gp_delay=      [KNL,BOOT]
 +                      Set wakeup interval for idle CPUs that have
 +                      RCU callbacks (RCU_FAST_NO_HZ=y).
  
 -      rcutree.jiffies_till_next_fqs= [KNL,BOOT]
 -                      Set delay between subsequent attempts to force
 -                      quiescent states.  Units are jiffies, minimum
 -                      value is one, and maximum value is HZ.
 +      rcutree.rcu_idle_lazy_gp_delay= [KNL,BOOT]
 +                      Set wakeup interval for idle CPUs that have
 +                      only "lazy" RCU callbacks (RCU_FAST_NO_HZ=y).
 +                      Lazy RCU callbacks are those which RCU can
 +                      prove do nothing more than free memory.
  
        rcutorture.fqs_duration= [KNL,BOOT]
                        Set duration of force_quiescent_state bursts.
                        Useful for devices that are detected asynchronously
                        (e.g. USB and MMC devices).
  
 +      rproc_mem=nn[KMG][@address]
 +                      [KNL,ARM,CMA] Remoteproc physical memory block.
 +                      Memory area to be used by remote processor image,
 +                      managed by CMA.
 +
        rw              [KNL] Mount root device read-write on boot
  
        S               [KNL] Run init in single mode
                        or other driver-specific files in the
                        Documentation/watchdog/ directory.
  
 +      workqueue.disable_numa
 +                      By default, all work items queued to unbound
 +                      workqueues are affine to the NUMA nodes they're
 +                      issued on, which results in better behavior in
 +                      general.  If NUMA affinity needs to be disabled for
 +                      whatever reason, this option can be used.  Note
 +                      that this also can be controlled per-workqueue for
 +                      workqueues visible under /sys/bus/workqueue/.
 +
        x2apic_phys     [X86-64,APIC] Use x2apic physical mode instead of
                        default x2apic cluster mode on platforms
                        supporting x2apic.
index 830183737b0ffd2001e85b1e444dc3c66723f33a,1d84be17ff2167253d0df115f1ff6ead90657666..21d02b0d907c1ada17aa3b1d0e13352cd2e4cbf3
@@@ -46,6 -46,7 +46,7 @@@
  #include "amd_iommu_proto.h"
  #include "amd_iommu_types.h"
  #include "irq_remapping.h"
+ #include "pci.h"
  
  #define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28))
  
@@@ -173,7 -174,7 +174,7 @@@ static inline u16 get_device_id(struct 
  {
        struct pci_dev *pdev = to_pci_dev(dev);
  
 -      return calc_devid(pdev->bus->number, pdev->devfn);
 +      return PCI_DEVID(pdev->bus->number, pdev->devfn);
  }
  
  static struct iommu_dev_data *get_dev_data(struct device *dev)
@@@ -263,12 -264,6 +264,6 @@@ static bool check_device(struct device 
        return true;
  }
  
- static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
- {
-       pci_dev_put(*from);
-       *from = to;
- }
  static struct pci_bus *find_hosted_bus(struct pci_bus *bus)
  {
        while (!bus->self) {
@@@ -649,26 -644,26 +644,26 @@@ retry
        case EVENT_TYPE_ILL_DEV:
                printk("ILLEGAL_DEV_TABLE_ENTRY device=%02x:%02x.%x "
                       "address=0x%016llx flags=0x%04x]\n",
 -                     PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
 +                     PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
                       address, flags);
                dump_dte_entry(devid);
                break;
        case EVENT_TYPE_IO_FAULT:
                printk("IO_PAGE_FAULT device=%02x:%02x.%x "
                       "domain=0x%04x address=0x%016llx flags=0x%04x]\n",
 -                     PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
 +                     PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
                       domid, address, flags);
                break;
        case EVENT_TYPE_DEV_TAB_ERR:
                printk("DEV_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
                       "address=0x%016llx flags=0x%04x]\n",
 -                     PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
 +                     PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
                       address, flags);
                break;
        case EVENT_TYPE_PAGE_TAB_ERR:
                printk("PAGE_TAB_HARDWARE_ERROR device=%02x:%02x.%x "
                       "domain=0x%04x address=0x%016llx flags=0x%04x]\n",
 -                     PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
 +                     PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
                       domid, address, flags);
                break;
        case EVENT_TYPE_ILL_CMD:
        case EVENT_TYPE_IOTLB_INV_TO:
                printk("IOTLB_INV_TIMEOUT device=%02x:%02x.%x "
                       "address=0x%016llx]\n",
 -                     PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
 +                     PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
                       address);
                break;
        case EVENT_TYPE_INV_DEV_REQ:
                printk("INVALID_DEVICE_REQUEST device=%02x:%02x.%x "
                       "address=0x%016llx flags=0x%04x]\n",
 -                     PCI_BUS(devid), PCI_SLOT(devid), PCI_FUNC(devid),
 +                     PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid),
                       address, flags);
                break;
        default:
  static void iommu_poll_events(struct amd_iommu *iommu)
  {
        u32 head, tail;
-       unsigned long flags;
-       spin_lock_irqsave(&iommu->lock, flags);
  
        head = readl(iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
        tail = readl(iommu->mmio_base + MMIO_EVT_TAIL_OFFSET);
        }
  
        writel(head, iommu->mmio_base + MMIO_EVT_HEAD_OFFSET);
-       spin_unlock_irqrestore(&iommu->lock, flags);
  }
  
  static void iommu_handle_ppr_entry(struct amd_iommu *iommu, u64 *raw)
  
  static void iommu_poll_ppr_log(struct amd_iommu *iommu)
  {
-       unsigned long flags;
        u32 head, tail;
  
        if (iommu->ppr_log == NULL)
                return;
  
-       /* enable ppr interrupts again */
-       writel(MMIO_STATUS_PPR_INT_MASK, iommu->mmio_base + MMIO_STATUS_OFFSET);
-       spin_lock_irqsave(&iommu->lock, flags);
        head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
        tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
  
                head = (head + PPR_ENTRY_SIZE) % PPR_LOG_SIZE;
                writel(head, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
  
-               /*
-                * Release iommu->lock because ppr-handling might need to
-                * re-acquire it
-                */
-               spin_unlock_irqrestore(&iommu->lock, flags);
                /* Handle PPR entry */
                iommu_handle_ppr_entry(iommu, entry);
  
-               spin_lock_irqsave(&iommu->lock, flags);
                /* Refresh ring-buffer information */
                head = readl(iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
                tail = readl(iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
        }
-       spin_unlock_irqrestore(&iommu->lock, flags);
  }
  
  irqreturn_t amd_iommu_int_thread(int irq, void *data)
  {
-       struct amd_iommu *iommu;
+       struct amd_iommu *iommu = (struct amd_iommu *) data;
+       u32 status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
  
-       for_each_iommu(iommu) {
-               iommu_poll_events(iommu);
-               iommu_poll_ppr_log(iommu);
-       }
+       while (status & (MMIO_STATUS_EVT_INT_MASK | MMIO_STATUS_PPR_INT_MASK)) {
+               /* Enable EVT and PPR interrupts again */
+               writel((MMIO_STATUS_EVT_INT_MASK | MMIO_STATUS_PPR_INT_MASK),
+                       iommu->mmio_base + MMIO_STATUS_OFFSET);
  
+               if (status & MMIO_STATUS_EVT_INT_MASK) {
+                       pr_devel("AMD-Vi: Processing IOMMU Event Log\n");
+                       iommu_poll_events(iommu);
+               }
+               if (status & MMIO_STATUS_PPR_INT_MASK) {
+                       pr_devel("AMD-Vi: Processing IOMMU PPR Log\n");
+                       iommu_poll_ppr_log(iommu);
+               }
+               /*
+                * Hardware bug: ERBT1312
+                * When re-enabling interrupt (by writing 1
+                * to clear the bit), the hardware might also try to set
+                * the interrupt bit in the event status register.
+                * In this scenario, the bit will be set, and disable
+                * subsequent interrupts.
+                *
+                * Workaround: The IOMMU driver should read back the
+                * status register and check if the interrupt bits are cleared.
+                * If not, driver will need to go through the interrupt handler
+                * again and re-clear the bits
+                */
+               status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET);
+       }
        return IRQ_HANDLED;
  }
  
@@@ -2838,24 -2838,6 +2838,6 @@@ static void unmap_page(struct device *d
        spin_unlock_irqrestore(&domain->lock, flags);
  }
  
- /*
-  * This is a special map_sg function which is used if we should map a
-  * device which is not handled by an AMD IOMMU in the system.
-  */
- static int map_sg_no_iommu(struct device *dev, struct scatterlist *sglist,
-                          int nelems, int dir)
- {
-       struct scatterlist *s;
-       int i;
-       for_each_sg(sglist, s, nelems, i) {
-               s->dma_address = (dma_addr_t)sg_phys(s);
-               s->dma_length  = s->length;
-       }
-       return nelems;
- }
  /*
   * The exported map_sg function for dma_ops (handles scatter-gather
   * lists).
@@@ -2875,9 -2857,7 +2857,7 @@@ static int map_sg(struct device *dev, s
        INC_STATS_COUNTER(cnt_map_sg);
  
        domain = get_domain(dev);
-       if (PTR_ERR(domain) == -EINVAL)
-               return map_sg_no_iommu(dev, sglist, nelems, dir);
-       else if (IS_ERR(domain))
+       if (IS_ERR(domain))
                return 0;
  
        dma_mask = *dev->dma_mask;
@@@ -3410,7 -3390,7 +3390,7 @@@ static size_t amd_iommu_unmap(struct io
  }
  
  static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
-                                         unsigned long iova)
+                                         dma_addr_t iova)
  {
        struct protection_domain *domain = dom->priv;
        unsigned long offset_mask;
@@@ -3947,6 -3927,9 +3927,9 @@@ static struct irq_remap_table *get_irq_
        if (!table)
                goto out;
  
+       /* Initialize table spin-lock */
+       spin_lock_init(&table->lock);
        if (ioapic)
                /* Keep the first 32 indexes free for IOAPIC interrupts */
                table->min_index = 32;
@@@ -4007,7 -3990,7 +3990,7 @@@ static int alloc_irq_index(struct irq_c
                        c = 0;
  
                if (c == count) {
-                       struct irq_2_iommu *irte_info;
+                       struct irq_2_irte *irte_info;
  
                        for (; c != 0; --c)
                                table->table[index - c + 1] = IRTE_ALLOCATED;
                        index -= count - 1;
  
                        cfg->remapped         = 1;
-                       irte_info             = &cfg->irq_2_iommu;
-                       irte_info->sub_handle = devid;
-                       irte_info->irte_index = index;
+                       irte_info             = &cfg->irq_2_irte;
+                       irte_info->devid      = devid;
+                       irte_info->index      = index;
  
                        goto out;
                }
@@@ -4098,7 -4081,7 +4081,7 @@@ static int setup_ioapic_entry(int irq, 
                              struct io_apic_irq_attr *attr)
  {
        struct irq_remap_table *table;
-       struct irq_2_iommu *irte_info;
+       struct irq_2_irte *irte_info;
        struct irq_cfg *cfg;
        union irte irte;
        int ioapic_id;
        if (!cfg)
                return -EINVAL;
  
-       irte_info = &cfg->irq_2_iommu;
+       irte_info = &cfg->irq_2_irte;
        ioapic_id = mpc_ioapic_id(attr->ioapic);
        devid     = get_ioapic_devid(ioapic_id);
  
  
        /* Setup IRQ remapping info */
        cfg->remapped         = 1;
-       irte_info->sub_handle = devid;
-       irte_info->irte_index = index;
+       irte_info->devid      = devid;
+       irte_info->index      = index;
  
        /* Setup IRTE for IOMMU */
        irte.val                = 0;
  static int set_affinity(struct irq_data *data, const struct cpumask *mask,
                        bool force)
  {
-       struct irq_2_iommu *irte_info;
+       struct irq_2_irte *irte_info;
        unsigned int dest, irq;
        struct irq_cfg *cfg;
        union irte irte;
  
        cfg       = data->chip_data;
        irq       = data->irq;
-       irte_info = &cfg->irq_2_iommu;
+       irte_info = &cfg->irq_2_irte;
  
        if (!cpumask_intersects(mask, cpu_online_mask))
                return -EINVAL;
  
-       if (get_irte(irte_info->sub_handle, irte_info->irte_index, &irte))
+       if (get_irte(irte_info->devid, irte_info->index, &irte))
                return -EBUSY;
  
        if (assign_irq_vector(irq, cfg, mask))
        irte.fields.vector      = cfg->vector;
        irte.fields.destination = dest;
  
-       modify_irte(irte_info->sub_handle, irte_info->irte_index, irte);
+       modify_irte(irte_info->devid, irte_info->index, irte);
  
        if (cfg->move_in_progress)
                send_cleanup_vector(cfg);
  
  static int free_irq(int irq)
  {
-       struct irq_2_iommu *irte_info;
+       struct irq_2_irte *irte_info;
        struct irq_cfg *cfg;
  
        cfg = irq_get_chip_data(irq);
        if (!cfg)
                return -EINVAL;
  
-       irte_info = &cfg->irq_2_iommu;
+       irte_info = &cfg->irq_2_irte;
  
-       free_irte(irte_info->sub_handle, irte_info->irte_index);
+       free_irte(irte_info->devid, irte_info->index);
  
        return 0;
  }
@@@ -4222,7 -4205,7 +4205,7 @@@ static void compose_msi_msg(struct pci_
                            unsigned int irq, unsigned int dest,
                            struct msi_msg *msg, u8 hpet_id)
  {
-       struct irq_2_iommu *irte_info;
+       struct irq_2_irte *irte_info;
        struct irq_cfg *cfg;
        union irte irte;
  
        if (!cfg)
                return;
  
-       irte_info = &cfg->irq_2_iommu;
+       irte_info = &cfg->irq_2_irte;
  
        irte.val                = 0;
        irte.fields.vector      = cfg->vector;
        irte.fields.dm          = apic->irq_dest_mode;
        irte.fields.valid       = 1;
  
-       modify_irte(irte_info->sub_handle, irte_info->irte_index, irte);
+       modify_irte(irte_info->devid, irte_info->index, irte);
  
        msg->address_hi = MSI_ADDR_BASE_HI;
        msg->address_lo = MSI_ADDR_BASE_LO;
-       msg->data       = irte_info->irte_index;
+       msg->data       = irte_info->index;
  }
  
  static int msi_alloc_irq(struct pci_dev *pdev, int irq, int nvec)
  static int msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
                         int index, int offset)
  {
-       struct irq_2_iommu *irte_info;
+       struct irq_2_irte *irte_info;
        struct irq_cfg *cfg;
        u16 devid;
  
                return 0;
  
        devid           = get_device_id(&pdev->dev);
-       irte_info       = &cfg->irq_2_iommu;
+       irte_info       = &cfg->irq_2_irte;
  
        cfg->remapped         = 1;
-       irte_info->sub_handle = devid;
-       irte_info->irte_index = index + offset;
+       irte_info->devid      = devid;
+       irte_info->index      = index + offset;
  
        return 0;
  }
  
  static int setup_hpet_msi(unsigned int irq, unsigned int id)
  {
-       struct irq_2_iommu *irte_info;
+       struct irq_2_irte *irte_info;
        struct irq_cfg *cfg;
        int index, devid;
  
        if (!cfg)
                return -EINVAL;
  
-       irte_info = &cfg->irq_2_iommu;
+       irte_info = &cfg->irq_2_irte;
        devid     = get_hpet_devid(id);
        if (devid < 0)
                return devid;
                return index;
  
        cfg->remapped         = 1;
-       irte_info->sub_handle = devid;
-       irte_info->irte_index = index;
+       irte_info->devid      = devid;
+       irte_info->index      = index;
  
        return 0;
  }
index 2f46881256a2d2cc0b6e5672e66a111326d021b0,9d23552a9619df9e7f8dff2f3cd4b20566e27d46..bf51abb78deed14392a4c3e80d180c41d1e76546
@@@ -213,6 -213,14 +213,14 @@@ enum iommu_init_state 
        IOMMU_INIT_ERROR,
  };
  
+ /* Early ioapic and hpet maps from kernel command line */
+ #define EARLY_MAP_SIZE                4
+ static struct devid_map __initdata early_ioapic_map[EARLY_MAP_SIZE];
+ static struct devid_map __initdata early_hpet_map[EARLY_MAP_SIZE];
+ static int __initdata early_ioapic_map_size;
+ static int __initdata early_hpet_map_size;
+ static bool __initdata cmdline_maps;
  static enum iommu_init_state init_state = IOMMU_START_STATE;
  
  static int amd_iommu_enable_interrupts(void);
@@@ -406,7 -414,7 +414,7 @@@ static int __init find_last_devid_on_pc
        u32 cap;
  
        cap = read_pci_config(bus, dev, fn, cap_ptr+MMIO_RANGE_OFFSET);
 -      update_last_devid(calc_devid(MMIO_GET_BUS(cap), MMIO_GET_LD(cap)));
 +      update_last_devid(PCI_DEVID(MMIO_GET_BUS(cap), MMIO_GET_LD(cap)));
  
        return 0;
  }
@@@ -423,7 -431,7 +431,7 @@@ static int __init find_last_devid_from_
        p += sizeof(*h);
        end += h->length;
  
 -      find_last_devid_on_pci(PCI_BUS(h->devid),
 +      find_last_devid_on_pci(PCI_BUS_NUM(h->devid),
                        PCI_SLOT(h->devid),
                        PCI_FUNC(h->devid),
                        h->cap_ptr);
@@@ -703,31 -711,66 +711,66 @@@ static void __init set_dev_entry_from_a
        set_iommu_for_device(iommu, devid);
  }
  
- static int add_special_device(u8 type, u8 id, u16 devid)
+ static int __init add_special_device(u8 type, u8 id, u16 devid, bool cmd_line)
  {
        struct devid_map *entry;
        struct list_head *list;
  
-       if (type != IVHD_SPECIAL_IOAPIC && type != IVHD_SPECIAL_HPET)
+       if (type == IVHD_SPECIAL_IOAPIC)
+               list = &ioapic_map;
+       else if (type == IVHD_SPECIAL_HPET)
+               list = &hpet_map;
+       else
                return -EINVAL;
  
+       list_for_each_entry(entry, list, list) {
+               if (!(entry->id == id && entry->cmd_line))
+                       continue;
+               pr_info("AMD-Vi: Command-line override present for %s id %d - ignoring\n",
+                       type == IVHD_SPECIAL_IOAPIC ? "IOAPIC" : "HPET", id);
+               return 0;
+       }
        entry = kzalloc(sizeof(*entry), GFP_KERNEL);
        if (!entry)
                return -ENOMEM;
  
-       entry->id    = id;
-       entry->devid = devid;
-       if (type == IVHD_SPECIAL_IOAPIC)
-               list = &ioapic_map;
-       else
-               list = &hpet_map;
+       entry->id       = id;
+       entry->devid    = devid;
+       entry->cmd_line = cmd_line;
  
        list_add_tail(&entry->list, list);
  
        return 0;
  }
  
+ static int __init add_early_maps(void)
+ {
+       int i, ret;
+       for (i = 0; i < early_ioapic_map_size; ++i) {
+               ret = add_special_device(IVHD_SPECIAL_IOAPIC,
+                                        early_ioapic_map[i].id,
+                                        early_ioapic_map[i].devid,
+                                        early_ioapic_map[i].cmd_line);
+               if (ret)
+                       return ret;
+       }
+       for (i = 0; i < early_hpet_map_size; ++i) {
+               ret = add_special_device(IVHD_SPECIAL_HPET,
+                                        early_hpet_map[i].id,
+                                        early_hpet_map[i].devid,
+                                        early_hpet_map[i].cmd_line);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+ }
  /*
   * Reads the device exclusion range from ACPI and initializes the IOMMU with
   * it
@@@ -764,6 -807,12 +807,12 @@@ static int __init init_iommu_from_acpi(
        u32 dev_i, ext_flags = 0;
        bool alias = false;
        struct ivhd_entry *e;
+       int ret;
+       ret = add_early_maps();
+       if (ret)
+               return ret;
  
        /*
         * First save the recommended feature enable bits from ACPI
  
                        DUMP_printk("  DEV_ALL\t\t\t first devid: %02x:%02x.%x"
                                    " last device %02x:%02x.%x flags: %02x\n",
 -                                  PCI_BUS(iommu->first_device),
 +                                  PCI_BUS_NUM(iommu->first_device),
                                    PCI_SLOT(iommu->first_device),
                                    PCI_FUNC(iommu->first_device),
 -                                  PCI_BUS(iommu->last_device),
 +                                  PCI_BUS_NUM(iommu->last_device),
                                    PCI_SLOT(iommu->last_device),
                                    PCI_FUNC(iommu->last_device),
                                    e->flags);
  
                        DUMP_printk("  DEV_SELECT\t\t\t devid: %02x:%02x.%x "
                                    "flags: %02x\n",
 -                                  PCI_BUS(e->devid),
 +                                  PCI_BUS_NUM(e->devid),
                                    PCI_SLOT(e->devid),
                                    PCI_FUNC(e->devid),
                                    e->flags);
  
                        DUMP_printk("  DEV_SELECT_RANGE_START\t "
                                    "devid: %02x:%02x.%x flags: %02x\n",
 -                                  PCI_BUS(e->devid),
 +                                  PCI_BUS_NUM(e->devid),
                                    PCI_SLOT(e->devid),
                                    PCI_FUNC(e->devid),
                                    e->flags);
  
                        DUMP_printk("  DEV_ALIAS\t\t\t devid: %02x:%02x.%x "
                                    "flags: %02x devid_to: %02x:%02x.%x\n",
 -                                  PCI_BUS(e->devid),
 +                                  PCI_BUS_NUM(e->devid),
                                    PCI_SLOT(e->devid),
                                    PCI_FUNC(e->devid),
                                    e->flags,
 -                                  PCI_BUS(e->ext >> 8),
 +                                  PCI_BUS_NUM(e->ext >> 8),
                                    PCI_SLOT(e->ext >> 8),
                                    PCI_FUNC(e->ext >> 8));
  
                        DUMP_printk("  DEV_ALIAS_RANGE\t\t "
                                    "devid: %02x:%02x.%x flags: %02x "
                                    "devid_to: %02x:%02x.%x\n",
 -                                  PCI_BUS(e->devid),
 +                                  PCI_BUS_NUM(e->devid),
                                    PCI_SLOT(e->devid),
                                    PCI_FUNC(e->devid),
                                    e->flags,
 -                                  PCI_BUS(e->ext >> 8),
 +                                  PCI_BUS_NUM(e->ext >> 8),
                                    PCI_SLOT(e->ext >> 8),
                                    PCI_FUNC(e->ext >> 8));
  
  
                        DUMP_printk("  DEV_EXT_SELECT\t\t devid: %02x:%02x.%x "
                                    "flags: %02x ext: %08x\n",
 -                                  PCI_BUS(e->devid),
 +                                  PCI_BUS_NUM(e->devid),
                                    PCI_SLOT(e->devid),
                                    PCI_FUNC(e->devid),
                                    e->flags, e->ext);
  
                        DUMP_printk("  DEV_EXT_SELECT_RANGE\t devid: "
                                    "%02x:%02x.%x flags: %02x ext: %08x\n",
 -                                  PCI_BUS(e->devid),
 +                                  PCI_BUS_NUM(e->devid),
                                    PCI_SLOT(e->devid),
                                    PCI_FUNC(e->devid),
                                    e->flags, e->ext);
                case IVHD_DEV_RANGE_END:
  
                        DUMP_printk("  DEV_RANGE_END\t\t devid: %02x:%02x.%x\n",
 -                                  PCI_BUS(e->devid),
 +                                  PCI_BUS_NUM(e->devid),
                                    PCI_SLOT(e->devid),
                                    PCI_FUNC(e->devid));
  
  
                        DUMP_printk("  DEV_SPECIAL(%s[%d])\t\tdevid: %02x:%02x.%x\n",
                                    var, (int)handle,
 -                                  PCI_BUS(devid),
 +                                  PCI_BUS_NUM(devid),
                                    PCI_SLOT(devid),
                                    PCI_FUNC(devid));
  
                        set_dev_entry_from_acpi(iommu, devid, e->flags, 0);
-                       ret = add_special_device(type, handle, devid);
+                       ret = add_special_device(type, handle, devid, false);
                        if (ret)
                                return ret;
                        break;
@@@ -1086,7 -1135,7 +1135,7 @@@ static int __init init_iommu_all(struc
  
                        DUMP_printk("device: %02x:%02x.%01x cap: %04x "
                                    "seg: %d flags: %01x info %04x\n",
 -                                  PCI_BUS(h->devid), PCI_SLOT(h->devid),
 +                                  PCI_BUS_NUM(h->devid), PCI_SLOT(h->devid),
                                    PCI_FUNC(h->devid), h->cap_ptr,
                                    h->pci_seg, h->flags, h->info);
                        DUMP_printk("       mmio-addr: %016llx\n",
@@@ -1116,7 -1165,7 +1165,7 @@@ static int iommu_init_pci(struct amd_io
        int cap_ptr = iommu->cap_ptr;
        u32 range, misc, low, high;
  
 -      iommu->dev = pci_get_bus_and_slot(PCI_BUS(iommu->devid),
 +      iommu->dev = pci_get_bus_and_slot(PCI_BUS_NUM(iommu->devid),
                                          iommu->devid & 0xff);
        if (!iommu->dev)
                return -ENODEV;
        pci_read_config_dword(iommu->dev, cap_ptr + MMIO_MISC_OFFSET,
                              &misc);
  
 -      iommu->first_device = calc_devid(MMIO_GET_BUS(range),
 +      iommu->first_device = PCI_DEVID(MMIO_GET_BUS(range),
                                         MMIO_GET_FD(range));
 -      iommu->last_device = calc_devid(MMIO_GET_BUS(range),
 +      iommu->last_device = PCI_DEVID(MMIO_GET_BUS(range),
                                        MMIO_GET_LD(range));
  
        if (!(iommu->cap & (1 << IOMMU_CAP_IOTLB)))
@@@ -1275,7 -1324,7 +1324,7 @@@ static int iommu_setup_msi(struct amd_i
                                 amd_iommu_int_handler,
                                 amd_iommu_int_thread,
                                 0, "AMD-Vi",
-                                iommu->dev);
+                                iommu);
  
        if (r) {
                pci_disable_msi(iommu->dev);
@@@ -1388,8 -1437,8 +1437,8 @@@ static int __init init_unity_map_range(
  
        DUMP_printk("%s devid_start: %02x:%02x.%x devid_end: %02x:%02x.%x"
                    " range_start: %016llx range_end: %016llx flags: %x\n", s,
 -                  PCI_BUS(e->devid_start), PCI_SLOT(e->devid_start),
 -                  PCI_FUNC(e->devid_start), PCI_BUS(e->devid_end),
 +                  PCI_BUS_NUM(e->devid_start), PCI_SLOT(e->devid_start),
 +                  PCI_FUNC(e->devid_start), PCI_BUS_NUM(e->devid_end),
                    PCI_SLOT(e->devid_end), PCI_FUNC(e->devid_end),
                    e->address_start, e->address_end, m->flags);
  
@@@ -1638,18 -1687,28 +1687,28 @@@ static void __init free_on_init_error(v
  
  static bool __init check_ioapic_information(void)
  {
+       const char *fw_bug = FW_BUG;
        bool ret, has_sb_ioapic;
        int idx;
  
        has_sb_ioapic = false;
        ret           = false;
  
+       /*
+        * If we have map overrides on the kernel command line the
+        * messages in this function might not describe firmware bugs
+        * anymore - so be careful
+        */
+       if (cmdline_maps)
+               fw_bug = "";
        for (idx = 0; idx < nr_ioapics; idx++) {
                int devid, id = mpc_ioapic_id(idx);
  
                devid = get_ioapic_devid(id);
                if (devid < 0) {
-                       pr_err(FW_BUG "AMD-Vi: IOAPIC[%d] not in IVRS table\n", id);
+                       pr_err("%sAMD-Vi: IOAPIC[%d] not in IVRS table\n",
+                               fw_bug, id);
                        ret = false;
                } else if (devid == IOAPIC_SB_DEVID) {
                        has_sb_ioapic = true;
                 * when the BIOS is buggy and provides us the wrong
                 * device id for the IOAPIC in the system.
                 */
-               pr_err(FW_BUG "AMD-Vi: No southbridge IOAPIC found in IVRS table\n");
+               pr_err("%sAMD-Vi: No southbridge IOAPIC found\n", fw_bug);
        }
  
        if (!ret)
-               pr_err("AMD-Vi: Disabling interrupt remapping due to BIOS Bug(s)\n");
+               pr_err("AMD-Vi: Disabling interrupt remapping\n");
  
        return ret;
  }
@@@ -1801,6 -1860,7 +1860,7 @@@ static int __init early_amd_iommu_init(
                 * Interrupt remapping enabled, create kmem_cache for the
                 * remapping tables.
                 */
+               ret = -ENOMEM;
                amd_iommu_irq_cache = kmem_cache_create("irq_remap_cache",
                                MAX_IRQS_PER_TABLE * sizeof(u32),
                                IRQ_TABLE_ALIGNMENT,
@@@ -2097,8 -2157,70 +2157,70 @@@ static int __init parse_amd_iommu_optio
        return 1;
  }
  
- __setup("amd_iommu_dump", parse_amd_iommu_dump);
- __setup("amd_iommu=", parse_amd_iommu_options);
+ static int __init parse_ivrs_ioapic(char *str)
+ {
+       unsigned int bus, dev, fn;
+       int ret, id, i;
+       u16 devid;
+       ret = sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn);
+       if (ret != 4) {
+               pr_err("AMD-Vi: Invalid command line: ivrs_ioapic%s\n", str);
+               return 1;
+       }
+       if (early_ioapic_map_size == EARLY_MAP_SIZE) {
+               pr_err("AMD-Vi: Early IOAPIC map overflow - ignoring ivrs_ioapic%s\n",
+                       str);
+               return 1;
+       }
+       devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7);
+       cmdline_maps                    = true;
+       i                               = early_ioapic_map_size++;
+       early_ioapic_map[i].id          = id;
+       early_ioapic_map[i].devid       = devid;
+       early_ioapic_map[i].cmd_line    = true;
+       return 1;
+ }
+ static int __init parse_ivrs_hpet(char *str)
+ {
+       unsigned int bus, dev, fn;
+       int ret, id, i;
+       u16 devid;
+       ret = sscanf(str, "[%d]=%x:%x.%x", &id, &bus, &dev, &fn);
+       if (ret != 4) {
+               pr_err("AMD-Vi: Invalid command line: ivrs_hpet%s\n", str);
+               return 1;
+       }
+       if (early_hpet_map_size == EARLY_MAP_SIZE) {
+               pr_err("AMD-Vi: Early HPET map overflow - ignoring ivrs_hpet%s\n",
+                       str);
+               return 1;
+       }
+       devid = ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fn & 0x7);
+       cmdline_maps                    = true;
+       i                               = early_hpet_map_size++;
+       early_hpet_map[i].id            = id;
+       early_hpet_map[i].devid         = devid;
+       early_hpet_map[i].cmd_line      = true;
+       return 1;
+ }
+ __setup("amd_iommu_dump",     parse_amd_iommu_dump);
+ __setup("amd_iommu=",         parse_amd_iommu_options);
+ __setup("ivrs_ioapic",                parse_ivrs_ioapic);
+ __setup("ivrs_hpet",          parse_ivrs_hpet);
  
  IOMMU_INIT_FINISH(amd_iommu_detect,
                  gart_iommu_hole_init,
index ec36cf63e0ca0a10c14c46fc610dfe3644795c4a,b81153fb9e6061849fd93f7c57a30ab53213ae1d..0285a215df162e18552d241ac753e0bd42c74600
@@@ -24,7 -24,6 +24,7 @@@
  #include <linux/mutex.h>
  #include <linux/list.h>
  #include <linux/spinlock.h>
 +#include <linux/pci.h>
  
  /*
   * Maximum number of IOMMUs supported
  #define PASID_MASK            0x000fffff
  
  /* MMIO status bits */
+ #define MMIO_STATUS_EVT_INT_MASK      (1 << 1)
  #define MMIO_STATUS_COM_WAIT_INT_MASK (1 << 2)
  #define MMIO_STATUS_PPR_INT_MASK      (1 << 6)
  
  
  #define MAX_DOMAIN_ID 65536
  
 -/* FIXME: move this macro to <linux/pci.h> */
 -#define PCI_BUS(x) (((x) >> 8) & 0xff)
 -
  /* Protection domain flags */
  #define PD_DMA_OPS_MASK               (1UL << 0) /* domain used for dma_ops */
  #define PD_DEFAULT_MASK               (1UL << 1) /* domain is a default dma_ops
@@@ -589,6 -592,7 +590,7 @@@ struct devid_map 
        struct list_head list;
        u8 id;
        u16 devid;
+       bool cmd_line;
  };
  
  /* Map HPET and IOAPIC ids to the devid used by the IOMMU */
@@@ -701,6 -705,13 +703,6 @@@ extern int amd_iommu_max_glx_val
   */
  extern void iommu_flush_all_caches(struct amd_iommu *iommu);
  
 -/* takes bus and device/function and returns the device id
 - * FIXME: should that be in generic PCI code? */
 -static inline u16 calc_devid(u8 bus, u8 devfn)
 -{
 -      return (((u16)bus) << 8) | devfn;
 -}
 -
  static inline int get_ioapic_devid(int id)
  {
        struct devid_map *entry;