From: "Aviv B.D" <bd.aviv@gmail.com>
To: qemu-devel@nongnu.org
Cc: "Michael S. Tsirkin" <mst@redhat.com>,
Alex Williamson <alex.williamson@redhat.com>,
Peter Xu <peterx@redhat.com>, Jan Kiszka <jan.kiszka@siemens.com>,
Aviv Ben-David <bd.aviv@gmail.com>
Subject: [Qemu-devel] [PATCH v5 3/3] IOMMU: enable intel_iommu map and unmap notifiers
Date: Fri, 21 Oct 2016 00:07:21 +0300 [thread overview]
Message-ID: <1476997641-29648-4-git-send-email-bd.aviv@gmail.com> (raw)
In-Reply-To: <1476997641-29648-1-git-send-email-bd.aviv@gmail.com>
From: "Aviv Ben-David" <bd.aviv@gmail.com>
Adds a list of registered vtd_as's to intel iommu state to save
iteration over each PCI device in a search of the corrosponding domain.
Signed-off-by: Aviv Ben-David <bd.aviv@gmail.com>
---
hw/i386/intel_iommu.c | 109 ++++++++++++++++++++++++++++++++++++++---
hw/i386/intel_iommu_internal.h | 2 +
include/hw/i386/intel_iommu.h | 9 ++++
3 files changed, 112 insertions(+), 8 deletions(-)
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 0821079..46e9b8e 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -54,6 +54,9 @@ static int vtd_dbgflags = VTD_DBGBIT(GENERAL) | VTD_DBGBIT(CSR);
#define VTD_DPRINTF(what, fmt, ...) do {} while (0)
#endif
+static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num,
+ uint8_t devfn, VTDContextEntry *ce);
+
static void vtd_define_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val,
uint64_t wmask, uint64_t w1cmask)
{
@@ -145,6 +148,23 @@ static uint64_t vtd_set_clear_mask_quad(IntelIOMMUState *s, hwaddr addr,
return new_val;
}
+static int vtd_get_did_dev(IntelIOMMUState *s, uint8_t bus_num, uint8_t devfn,
+ uint16_t *domain_id)
+{
+ VTDContextEntry ce;
+ int ret_fr;
+
+ assert(domain_id);
+
+ ret_fr = vtd_dev_to_context_entry(s, bus_num, devfn, &ce);
+ if (ret_fr) {
+ return -1;
+ }
+
+ *domain_id = VTD_CONTEXT_ENTRY_DID(ce.hi);
+ return 0;
+}
+
/* GHashTable functions */
static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2)
{
@@ -679,10 +699,10 @@ static int vtd_gpa_to_slpte(VTDContextEntry *ce, uint64_t gpa,
}
*reads = (*reads) && (slpte & VTD_SL_R);
*writes = (*writes) && (slpte & VTD_SL_W);
- if (!(slpte & access_right_check)) {
+ if (!(slpte & access_right_check) && !(flags & IOMMU_NO_FAIL)) {
VTD_DPRINTF(GENERAL, "error: lack of %s permission for "
"gpa 0x%"PRIx64 " slpte 0x%"PRIx64,
- (flags == IOMMU_WO ? "write" : "read"), gpa, slpte);
+ (flags & IOMMU_WO ? "write" : "read"), gpa, slpte);
return (flags & IOMMU_WO) ? -VTD_FR_WRITE : -VTD_FR_READ;
}
if (vtd_slpte_nonzero_rsvd(slpte, level)) {
@@ -1064,6 +1084,55 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id)
&domain_id);
}
+static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s,
+ uint16_t domain_id, hwaddr addr,
+ uint8_t am)
+{
+ IntelIOMMUNotifierNode *node;
+
+ QLIST_FOREACH(node, &(s->notifiers_list), next) {
+ VTDAddressSpace *vtd_as = node->vtd_as;
+ uint16_t vfio_domain_id;
+ int ret = vtd_get_did_dev(s, pci_bus_num(vtd_as->bus), vtd_as->devfn,
+ &vfio_domain_id);
+ if (!ret && domain_id == vfio_domain_id) {
+ IOMMUTLBEntry entry;
+
+ /* notify unmap */
+ if (node->notifier_flag & IOMMU_NOTIFIER_UNMAP) {
+ VTD_DPRINTF(GENERAL, "Remove addr 0x%"PRIx64 " mask %d",
+ addr, am);
+ entry.target_as = &address_space_memory;
+ entry.iova = addr & VTD_PAGE_MASK_4K;
+ entry.translated_addr = 0;
+ entry.addr_mask = ~VTD_PAGE_MASK(VTD_PAGE_SHIFT_4K + am);
+ entry.perm = IOMMU_NONE;
+ memory_region_notify_iommu(&node->vtd_as->iommu, entry);
+ }
+
+ /* notify map */
+ if (node->notifier_flag & IOMMU_NOTIFIER_MAP) {
+ hwaddr original_addr = addr;
+ VTD_DPRINTF(GENERAL, "add addr 0x%"PRIx64 " mask %d", addr, am);
+ while (addr < original_addr + (1 << am) * VTD_PAGE_SIZE) {
+ /* call to vtd_iommu_translate */
+ IOMMUTLBEntry entry = s->iommu_ops.translate(
+ &node->vtd_as->iommu,
+ addr,
+ IOMMU_NO_FAIL);
+ if (entry.perm != IOMMU_NONE) {
+ addr += entry.addr_mask + 1;
+ memory_region_notify_iommu(&node->vtd_as->iommu, entry);
+ } else {
+ addr += VTD_PAGE_SIZE;
+ }
+ }
+ }
+ }
+ }
+}
+
+
static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
hwaddr addr, uint8_t am)
{
@@ -1074,6 +1143,8 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
info.addr = addr;
info.mask = ~((1 << am) - 1);
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info);
+
+ vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am);
}
/* Flush IOTLB
@@ -1999,15 +2070,37 @@ static void vtd_iommu_notify_flag_changed(MemoryRegion *iommu,
IOMMUNotifierFlag new)
{
VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu);
+ IntelIOMMUState *s = vtd_as->iommu_state;
+ IntelIOMMUNotifierNode *node = NULL;
+ IntelIOMMUNotifierNode *next_node = NULL;
- if (new & IOMMU_NOTIFIER_MAP) {
- error_report("Device at bus %s addr %02x.%d requires iommu "
- "notifier which is currently not supported by "
- "intel-iommu emulation",
- vtd_as->bus->qbus.name, PCI_SLOT(vtd_as->devfn),
- PCI_FUNC(vtd_as->devfn));
+ if (!s->cache_mode_enabled && new & IOMMU_NOTIFIER_MAP) {
+ error_report("We need to set cache_mode=1 for intel-iommu to enable "
+ "device assignment with IOMMU protection.");
exit(1);
}
+
+ /* Add new ndoe if no mapping was exising before this call */
+ if (old == IOMMU_NOTIFIER_NONE) {
+ node = g_malloc0(sizeof(*node));
+ node->vtd_as = vtd_as;
+ node->notifier_flag = new;
+ QLIST_INSERT_HEAD(&s->notifiers_list, node, next);
+ return;
+ }
+
+ /* update notifier node with new flags */
+ QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) {
+ if (node->vtd_as == vtd_as) {
+ if (new == IOMMU_NOTIFIER_NONE) {
+ QLIST_REMOVE(node, next);
+ g_free(node);
+ } else {
+ node->notifier_flag = new;
+ }
+ return;
+ }
+ }
}
static const VMStateDescription vtd_vmstate = {
diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h
index 35d9f3a..96df4ae 100644
--- a/hw/i386/intel_iommu_internal.h
+++ b/hw/i386/intel_iommu_internal.h
@@ -381,6 +381,8 @@ typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo;
#define VTD_PAGE_SHIFT_1G 30
#define VTD_PAGE_MASK_1G (~((1ULL << VTD_PAGE_SHIFT_1G) - 1))
+#define VTD_PAGE_MASK(shift) (~((1ULL << (shift)) - 1))
+
struct VTDRootEntry {
uint64_t val;
uint64_t rsvd;
diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
index 42d293f..ebe9250 100644
--- a/include/hw/i386/intel_iommu.h
+++ b/include/hw/i386/intel_iommu.h
@@ -63,6 +63,7 @@ typedef union VTD_IR_TableEntry VTD_IR_TableEntry;
typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress;
typedef struct VTDIrq VTDIrq;
typedef struct VTD_MSIMessage VTD_MSIMessage;
+typedef struct IntelIOMMUNotifierNode IntelIOMMUNotifierNode;
/* Context-Entry */
struct VTDContextEntry {
@@ -248,6 +249,12 @@ struct VTD_MSIMessage {
/* When IR is enabled, all MSI/MSI-X data bits should be zero */
#define VTD_IR_MSI_DATA (0)
+struct IntelIOMMUNotifierNode {
+ VTDAddressSpace *vtd_as;
+ IOMMUNotifierFlag notifier_flag;
+ QLIST_ENTRY(IntelIOMMUNotifierNode) next;
+};
+
/* The iommu (DMAR) device state struct */
struct IntelIOMMUState {
X86IOMMUState x86_iommu;
@@ -285,6 +292,8 @@ struct IntelIOMMUState {
MemoryRegionIOMMUOps iommu_ops;
GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */
VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */
+ /* list of registered notifiers */
+ QLIST_HEAD(, IntelIOMMUNotifierNode) notifiers_list;
/* interrupt remapping */
bool intr_enabled; /* Whether guest enabled IR */
--
1.9.1
next prev parent reply other threads:[~2016-10-20 21:07 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-10-20 21:07 [Qemu-devel] [PATCH v5 0/3] IOMMU: intel_iommu support map and unmap notifications Aviv B.D
2016-10-20 21:07 ` [Qemu-devel] [PATCH v5 1/3] IOMMU: add option to enable VTD_CAP_CM to vIOMMU capility exposoed to guest Aviv B.D
2016-10-20 21:07 ` [Qemu-devel] [PATCH v5 2/3] IOMMU: change iommu_op->translate's is_write to flags, add support to NO_FAIL flag mode Aviv B.D
2016-10-20 21:07 ` Aviv B.D [this message]
2016-10-26 16:37 ` [Qemu-devel] [PATCH v5 0/3] IOMMU: intel_iommu support map and unmap notifications Chao Gao
2016-10-30 10:18 ` Aviv B.D.
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=1476997641-29648-4-git-send-email-bd.aviv@gmail.com \
--to=bd.aviv@gmail.com \
--cc=alex.williamson@redhat.com \
--cc=jan.kiszka@siemens.com \
--cc=mst@redhat.com \
--cc=peterx@redhat.com \
--cc=qemu-devel@nongnu.org \
/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.