ia64/xen-unstable

changeset 19463:57b733f66531

vtd: fix multiple Dom0 S3 on hosts that support Queued Invalidation.

On such hosts we can't do multiple Dom0 S3 when VT-d is enabled.
The cause is: during the first S3 resume, init_vtd_hw() initializes
the invalidation function pointers to the register-based ones and later
enable_qinval() forgets to overwrite the flush function pointers to
queued-based ones, so actually Queued Invalidaton is enabled, but we
actually use the register-based invalidation function! Later during
the second Dom0 S3, in iommu_suspend() -> iommu_flush_all(), we try to
use the register-based invalidation functions to perform global flush
while Queued Invalidation is enabled, and this can cause a host reset
because VT-d spec says: when the queued invalidation is enabled,
software must submit invalidation commands only through the IQ (and
not through any invalidation command registers).

The attached patch fixes the buggy enable_qinval(). And in
iommu_resume(), we invoke iommu_flush_all() for safety.

Signed-off-by: Dexuan Cui <dexuan.cui@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Mar 31 11:54:12 2009 +0100 (2009-03-31)
parents f6a2bf60d49c
children 132718080e64
files xen/drivers/passthrough/vtd/iommu.c xen/drivers/passthrough/vtd/qinval.c
line diff
     1.1 --- a/xen/drivers/passthrough/vtd/iommu.c	Tue Mar 31 11:51:56 2009 +0100
     1.2 +++ b/xen/drivers/passthrough/vtd/iommu.c	Tue Mar 31 11:54:12 2009 +0100
     1.3 @@ -1953,16 +1953,34 @@ void iommu_resume(void)
     1.4  {
     1.5      struct acpi_drhd_unit *drhd;
     1.6      struct iommu *iommu;
     1.7 +    struct iommu_flush *flush;
     1.8      u32 i;
     1.9  
    1.10      if ( !vtd_enabled )
    1.11          return;
    1.12  
    1.13 +    /* Re-initialize the register-based flush functions.
    1.14 +     * In iommu_flush_all(), we invoke iommu_flush_{context,iotlb}_global(),
    1.15 +     * but at this point, on hosts that support QI(Queued Invalidation), QI
    1.16 +     * hasn't been re-enabed yet, so for now let's use the register-based
    1.17 +     * invalidation method before invoking init_vtd_hw().
    1.18 +     */
    1.19 +    if ( iommu_qinval )
    1.20 +    {
    1.21 +        for_each_drhd_unit ( drhd )
    1.22 +        {
    1.23 +            iommu = drhd->iommu;
    1.24 +            flush = iommu_get_flush(iommu);
    1.25 +            flush->context = flush_context_reg;
    1.26 +            flush->iotlb = flush_iotlb_reg;
    1.27 +        }
    1.28 +    }
    1.29 +
    1.30      /* Not sure whether the flush operation is required to meet iommu
    1.31       * specification. Note that BIOS also executes in S3 resume and iommu may
    1.32       * be touched again, so let us do the flush operation for safety.
    1.33       */
    1.34 -    flush_all_cache();
    1.35 +    iommu_flush_all();
    1.36  
    1.37      if ( init_vtd_hw() != 0  && force_iommu )
    1.38           panic("IOMMU setup failed, crash Xen for security purpose!\n");
     2.1 --- a/xen/drivers/passthrough/vtd/qinval.c	Tue Mar 31 11:51:56 2009 +0100
     2.2 +++ b/xen/drivers/passthrough/vtd/qinval.c	Tue Mar 31 11:54:12 2009 +0100
     2.3 @@ -432,10 +432,11 @@ int enable_qinval(struct iommu *iommu)
     2.4                      "Cannot allocate memory for qi_ctrl->qinval_maddr\n");
     2.5              return -ENOMEM;
     2.6          }
     2.7 -        flush->context = flush_context_qi;
     2.8 -        flush->iotlb = flush_iotlb_qi;
     2.9      }
    2.10  
    2.11 +    flush->context = flush_context_qi;
    2.12 +    flush->iotlb = flush_iotlb_qi;
    2.13 +
    2.14      /* Setup Invalidation Queue Address(IQA) register with the
    2.15       * address of the page we just allocated.  QS field at
    2.16       * bits[2:0] to indicate size of queue is one 4KB page.