All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michael S. Tsirkin" <mst@redhat.com>
To: qemu-devel@nongnu.org
Cc: Peter Maydell <peter.maydell@linaro.org>,
	Peter Xu <peterx@redhat.com>,
	QEMU Stable <qemu-stable@nongnu.org>, Fam Zheng <famz@redhat.com>,
	Marcel Apfelbaum <marcel.apfelbaum@gmail.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	Richard Henderson <rth@twiddle.net>,
	Eduardo Habkost <ehabkost@redhat.com>
Subject: [Qemu-devel] [PULL 22/28] intel-iommu: add iommu lock
Date: Wed, 23 May 2018 17:43:20 +0300	[thread overview]
Message-ID: <1527086545-68024-23-git-send-email-mst@redhat.com> (raw)
In-Reply-To: <1527086545-68024-1-git-send-email-mst@redhat.com>

From: Peter Xu <peterx@redhat.com>

SECURITY IMPLICATION: this patch fixes a potential race when multiple
threads access the IOMMU IOTLB cache.

Add a per-iommu big lock to protect IOMMU status.  Currently the only
thing to be protected is the IOTLB/context cache, since that can be
accessed even without BQL, e.g., in IO dataplane.

Note that we don't need to protect device page tables since that's fully
controlled by the guest kernel.  However there is still possibility that
malicious drivers will program the device to not obey the rule.  In that
case QEMU can't really do anything useful, instead the guest itself will
be responsible for all uncertainties.

CC: QEMU Stable <qemu-stable@nongnu.org>
Reported-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
 include/hw/i386/intel_iommu.h |  6 +++++
 hw/i386/intel_iommu.c         | 56 ++++++++++++++++++++++++++++++++++++-------
 2 files changed, 53 insertions(+), 9 deletions(-)

diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
index 032e33b..016e74b 100644
--- a/include/hw/i386/intel_iommu.h
+++ b/include/hw/i386/intel_iommu.h
@@ -300,6 +300,12 @@ struct IntelIOMMUState {
     OnOffAuto intr_eim;             /* Toggle for EIM cabability */
     bool buggy_eim;                 /* Force buggy EIM unless eim=off */
     uint8_t aw_bits;                /* Host/IOVA address width (in bits) */
+
+    /*
+     * Protects IOMMU states in general.  Currently it protects the
+     * per-IOMMU IOTLB cache, and context entry cache in VTDAddressSpace.
+     */
+    QemuMutex iommu_lock;
 };
 
 /* Find the VTD Address space associated with the given bus pointer,
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 3df9045..8d4069d 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -128,6 +128,16 @@ static uint64_t vtd_set_clear_mask_quad(IntelIOMMUState *s, hwaddr addr,
     return new_val;
 }
 
+static inline void vtd_iommu_lock(IntelIOMMUState *s)
+{
+    qemu_mutex_lock(&s->iommu_lock);
+}
+
+static inline void vtd_iommu_unlock(IntelIOMMUState *s)
+{
+    qemu_mutex_unlock(&s->iommu_lock);
+}
+
 /* GHashTable functions */
 static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2)
 {
@@ -172,9 +182,9 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value,
 }
 
 /* Reset all the gen of VTDAddressSpace to zero and set the gen of
- * IntelIOMMUState to 1.
+ * IntelIOMMUState to 1.  Must be called with IOMMU lock held.
  */
-static void vtd_reset_context_cache(IntelIOMMUState *s)
+static void vtd_reset_context_cache_locked(IntelIOMMUState *s)
 {
     VTDAddressSpace *vtd_as;
     VTDBus *vtd_bus;
@@ -197,12 +207,20 @@ static void vtd_reset_context_cache(IntelIOMMUState *s)
     s->context_cache_gen = 1;
 }
 
-static void vtd_reset_iotlb(IntelIOMMUState *s)
+/* Must be called with IOMMU lock held. */
+static void vtd_reset_iotlb_locked(IntelIOMMUState *s)
 {
     assert(s->iotlb);
     g_hash_table_remove_all(s->iotlb);
 }
 
+static void vtd_reset_iotlb(IntelIOMMUState *s)
+{
+    vtd_iommu_lock(s);
+    vtd_reset_iotlb_locked(s);
+    vtd_iommu_unlock(s);
+}
+
 static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id,
                                   uint32_t level)
 {
@@ -215,6 +233,7 @@ static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level)
     return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K;
 }
 
+/* Must be called with IOMMU lock held */
 static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id,
                                        hwaddr addr)
 {
@@ -235,6 +254,7 @@ out:
     return entry;
 }
 
+/* Must be with IOMMU lock held */
 static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
                              uint16_t domain_id, hwaddr addr, uint64_t slpte,
                              uint8_t access_flags, uint32_t level)
@@ -246,7 +266,7 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
     trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id);
     if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) {
         trace_vtd_iotlb_reset("iotlb exceeds size limit");
-        vtd_reset_iotlb(s);
+        vtd_reset_iotlb_locked(s);
     }
 
     entry->gfn = gfn;
@@ -1106,7 +1126,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
     IntelIOMMUState *s = vtd_as->iommu_state;
     VTDContextEntry ce;
     uint8_t bus_num = pci_bus_num(bus);
-    VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry;
+    VTDContextCacheEntry *cc_entry;
     uint64_t slpte, page_mask;
     uint32_t level;
     uint16_t source_id = vtd_make_source_id(bus_num, devfn);
@@ -1123,6 +1143,10 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
      */
     assert(!vtd_is_interrupt_addr(addr));
 
+    vtd_iommu_lock(s);
+
+    cc_entry = &vtd_as->context_cache_entry;
+
     /* Try to fetch slpte form IOTLB */
     iotlb_entry = vtd_lookup_iotlb(s, source_id, addr);
     if (iotlb_entry) {
@@ -1182,7 +1206,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
          * IOMMU region can be swapped back.
          */
         vtd_pt_enable_fast_path(s, source_id);
-
+        vtd_iommu_unlock(s);
         return true;
     }
 
@@ -1203,6 +1227,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
     vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte,
                      access_flags, level);
 out:
+    vtd_iommu_unlock(s);
     entry->iova = addr & page_mask;
     entry->translated_addr = vtd_get_slpte_addr(slpte, s->aw_bits) & page_mask;
     entry->addr_mask = ~page_mask;
@@ -1210,6 +1235,7 @@ out:
     return true;
 
 error:
+    vtd_iommu_unlock(s);
     entry->iova = 0;
     entry->translated_addr = 0;
     entry->addr_mask = 0;
@@ -1258,10 +1284,13 @@ static void vtd_iommu_replay_all(IntelIOMMUState *s)
 static void vtd_context_global_invalidate(IntelIOMMUState *s)
 {
     trace_vtd_inv_desc_cc_global();
+    /* Protects context cache */
+    vtd_iommu_lock(s);
     s->context_cache_gen++;
     if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) {
-        vtd_reset_context_cache(s);
+        vtd_reset_context_cache_locked(s);
     }
+    vtd_iommu_unlock(s);
     vtd_switch_address_space_all(s);
     /*
      * From VT-d spec 6.5.2.1, a global context entry invalidation
@@ -1313,7 +1342,9 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
             if (vtd_as && ((devfn_it & mask) == (devfn & mask))) {
                 trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it),
                                              VTD_PCI_FUNC(devfn_it));
+                vtd_iommu_lock(s);
                 vtd_as->context_cache_entry.context_cache_gen = 0;
+                vtd_iommu_unlock(s);
                 /*
                  * Do switch address space when needed, in case if the
                  * device passthrough bit is switched.
@@ -1377,8 +1408,10 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id)
 
     trace_vtd_inv_desc_iotlb_domain(domain_id);
 
+    vtd_iommu_lock(s);
     g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain,
                                 &domain_id);
+    vtd_iommu_unlock(s);
 
     QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) {
         if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus),
@@ -1426,7 +1459,9 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
     info.domain_id = domain_id;
     info.addr = addr;
     info.mask = ~((1 << am) - 1);
+    vtd_iommu_lock(s);
     g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info);
+    vtd_iommu_unlock(s);
     vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am);
 }
 
@@ -2929,8 +2964,10 @@ static void vtd_init(IntelIOMMUState *s)
         s->cap |= VTD_CAP_CM;
     }
 
-    vtd_reset_context_cache(s);
-    vtd_reset_iotlb(s);
+    vtd_iommu_lock(s);
+    vtd_reset_context_cache_locked(s);
+    vtd_reset_iotlb_locked(s);
+    vtd_iommu_unlock(s);
 
     /* Define registers with default values and bit semantics */
     vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0);
@@ -3070,6 +3107,7 @@ static void vtd_realize(DeviceState *dev, Error **errp)
     }
 
     QLIST_INIT(&s->vtd_as_with_notifiers);
+    qemu_mutex_init(&s->iommu_lock);
     memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
     memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
                           "intel_iommu", DMAR_REG_SIZE);
-- 
MST

  parent reply	other threads:[~2018-05-23 14:43 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-05-23 14:42 [Qemu-devel] [PULL 00/28] pc, pci, virtio, vhost: fixes, features Michael S. Tsirkin
2018-05-23 14:42 ` [Qemu-devel] [PULL 01/28] hw/pci-host/q35: Replace hardcoded value with macro Michael S. Tsirkin
2018-05-23 14:42 ` [Qemu-devel] [PULL 02/28] allocate pci id for mdpy Michael S. Tsirkin
2018-05-23 14:42 ` [Qemu-devel] [PULL 04/28] vhost: add trace for IOTLB miss Michael S. Tsirkin
2018-05-23 14:42 ` [Qemu-devel] [PULL 03/28] virtio-balloon: add hugetlb page allocation counts Michael S. Tsirkin
2018-05-23 14:42 ` [Qemu-devel] [PULL 05/28] update-linux-headers.sh: drop kvm_para.h hacks Michael S. Tsirkin
2018-05-23 14:42 ` [Qemu-devel] [PULL 06/28] include/standard-headers: add asm-x86/kvm_para.h Michael S. Tsirkin
2018-05-23 14:43 ` [PULL 07/28] x86/cpu: use standard-headers/asm-x86.kvm_para.h Michael S. Tsirkin
2018-05-23 14:43   ` [Qemu-devel] " Michael S. Tsirkin
2018-05-25 11:06   ` Peter Maydell
2018-05-25 11:06     ` [Qemu-devel] " Peter Maydell
2018-05-25 11:53     ` Peter Maydell
2018-05-25 11:53       ` [Qemu-devel] " Peter Maydell
2018-05-25 12:18       ` Michael S. Tsirkin
2018-05-25 12:18         ` [Qemu-devel] " Michael S. Tsirkin
2018-05-25 12:21         ` Peter Maydell
2018-05-25 12:21           ` [Qemu-devel] " Peter Maydell
2018-05-25 12:27           ` Michael S. Tsirkin
2018-05-25 12:27             ` [Qemu-devel] " Michael S. Tsirkin
2018-05-25 12:30             ` Peter Maydell
2018-05-25 12:30               ` [Qemu-devel] " Peter Maydell
2018-05-25 12:35               ` Michael S. Tsirkin
2018-05-25 12:35                 ` [Qemu-devel] " Michael S. Tsirkin
2018-05-25 12:38                 ` Peter Maydell
2018-05-25 12:38                   ` [Qemu-devel] " Peter Maydell
2018-05-25 12:19     ` Michael S. Tsirkin
2018-05-25 12:19       ` [Qemu-devel] " Michael S. Tsirkin
2018-05-25 14:13     ` Paolo Bonzini
2018-05-25 14:13       ` [Qemu-devel] " Paolo Bonzini
2018-05-23 14:43 ` [Qemu-devel] [PULL 08/28] linux-headers: drop kvm_para.h Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 09/28] update-linux-headers.sh: unistd.h, kvm consistency Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 10/28] linux-headers: add unistd.h on all arches Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 12/28] vhost-user: add Net prefix to internal state structure Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 11/28] linux-headers: add kvm header for mips Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 13/28] vhost-user: support receiving file descriptors in slave_read Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 15/28] vhost-user+postcopy: Use qemu_set_nonblock Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 14/28] virtio: support setting memory region based host notifier Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 16/28] libvhost-user: Send messages with no data Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 17/28] hw/virtio: Fix brace Werror with clang 6.0.0 Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 18/28] contrib/vhost-user-blk: enable protocol feature for vhost-user-blk Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 19/28] nvdimm: fix typo in label-size definition Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 20/28] intel-iommu: send PSI always even if across PDEs Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 21/28] intel-iommu: remove IntelIOMMUNotifierNode Michael S. Tsirkin
2018-05-23 14:43 ` Michael S. Tsirkin [this message]
2018-05-23 14:43 ` [Qemu-devel] [PULL 23/28] intel-iommu: only do page walk for MAP notifiers Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 24/28] intel-iommu: introduce vtd_page_walk_info Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 25/28] intel-iommu: pass in address space when page walk Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 26/28] intel-iommu: trace domain id during " Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 27/28] util: implement simple iova tree Michael S. Tsirkin
2018-05-23 14:43 ` [Qemu-devel] [PULL 28/28] intel-iommu: rework the page walk logic Michael S. Tsirkin
2018-05-23 15:17 ` [Qemu-devel] [PULL 00/28] pc, pci, virtio, vhost: fixes, features no-reply
2018-05-24 14:18 ` Peter Maydell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1527086545-68024-23-git-send-email-mst@redhat.com \
    --to=mst@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=famz@redhat.com \
    --cc=marcel.apfelbaum@gmail.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=peterx@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-stable@nongnu.org \
    --cc=rth@twiddle.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.