diff options
author | Ladi Prosek | 2017-06-19 09:31:16 +0200 |
---|---|---|
committer | Michael S. Tsirkin | 2017-07-03 21:29:48 +0200 |
commit | 8991c460be5a0811194fd4d2b49ba7146a23526b (patch) | |
tree | 4981ef1ad33d0e16d9dc42c1966086f94e3e3c58 /hw/i386/intel_iommu.c | |
parent | hw/pci-bridge/dec: Classify the DEC PCI bridge as bridge device (diff) | |
download | qemu-8991c460be5a0811194fd4d2b49ba7146a23526b.tar.gz qemu-8991c460be5a0811194fd4d2b49ba7146a23526b.tar.xz qemu-8991c460be5a0811194fd4d2b49ba7146a23526b.zip |
intel_iommu: relax iq tail check on VTD_GCMD_QIE enable
The VT-d spec (section 6.5.2) prescribes software to zero the
Invalidation Queue Tail Register before enabling the VTD_GCMD_QIE
Global Command Register bit. Windows Server 2012 R2 and possibly
other older Windows versions violate the protocol and set a
non-zero queue tail first, which in effect makes them crash early
on boot with -device intel-iommu,intremap=on.
This commit relaxes the check and instead of failing to enable
VTD_GCMD_QIE with vtd_err_qi_enable, it behaves as if the tail
register was set just after enabling VTD_GCMD_QIE
(see vtd_handle_iqt_write).
Signed-off-by: Ladi Prosek <lprosek@redhat.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'hw/i386/intel_iommu.c')
-rw-r--r-- | hw/i386/intel_iommu.c | 33 |
1 files changed, 19 insertions, 14 deletions
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index a9b59bdce5..2ddf3bd5b5 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1450,10 +1450,7 @@ static uint64_t vtd_iotlb_flush(IntelIOMMUState *s, uint64_t val) return iaig; } -static inline bool vtd_queued_inv_enable_check(IntelIOMMUState *s) -{ - return s->iq_tail == 0; -} +static void vtd_fetch_inv_desc(IntelIOMMUState *s); static inline bool vtd_queued_inv_disable_check(IntelIOMMUState *s) { @@ -1468,16 +1465,24 @@ static void vtd_handle_gcmd_qie(IntelIOMMUState *s, bool en) trace_vtd_inv_qi_enable(en); if (en) { - if (vtd_queued_inv_enable_check(s)) { - s->iq = iqa_val & VTD_IQA_IQA_MASK; - /* 2^(x+8) entries */ - s->iq_size = 1UL << ((iqa_val & VTD_IQA_QS) + 8); - s->qi_enabled = true; - trace_vtd_inv_qi_setup(s->iq, s->iq_size); - /* Ok - report back to driver */ - vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_QIES); - } else { - trace_vtd_err_qi_enable(s->iq_tail); + s->iq = iqa_val & VTD_IQA_IQA_MASK; + /* 2^(x+8) entries */ + s->iq_size = 1UL << ((iqa_val & VTD_IQA_QS) + 8); + s->qi_enabled = true; + trace_vtd_inv_qi_setup(s->iq, s->iq_size); + /* Ok - report back to driver */ + vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_QIES); + + if (s->iq_tail != 0) { + /* + * This is a spec violation but Windows guests are known to set up + * Queued Invalidation this way so we allow the write and process + * Invalidation Descriptors right away. + */ + trace_vtd_warn_invalid_qi_tail(s->iq_tail); + if (!(vtd_get_long_raw(s, DMAR_FSTS_REG) & VTD_FSTS_IQE)) { + vtd_fetch_inv_desc(s); + } } } else { if (vtd_queued_inv_disable_check(s)) { |