Time to consider a common stuff for IOMMU. Let's start from an common IOMMU object (which should be inlayed in custom IOMMU implementations) and a notifier mechanism.
Let VT-d IOMMU be the first user. An example to use this per-iommu notifier: (when registering) iommu = address_space_iommu_get(pci_device_iommu_address_space(dev)); notifier = iommu_notifier_register(iommu, IOMMU_EVENT_SVM_PASID, func); ... (when notify) IOMMUEvent event = { .type = IOMMU_EVENT_SVM_PASID ... }; iommu_notify(iommu, &event); ... (when releasing) iommu_notifier_unregister(notifier); notifier = NULL; Signed-off-by: Peter Xu <pet...@redhat.com> --- hw/core/Makefile.objs | 1 + hw/core/iommu.c | 61 ++++++++++++++++++++++++++++++++++++ hw/i386/intel_iommu.c | 2 +- include/exec/memory.h | 10 +----- include/hw/core/iommu.h | 72 +++++++++++++++++++++++++++++++++++++++++++ include/hw/i386/intel_iommu.h | 2 ++ 6 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 hw/core/iommu.c create mode 100644 include/hw/core/iommu.h diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 91450b2..85cca44 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -5,6 +5,7 @@ common-obj-y += fw-path-provider.o # irq.o needed for qdev GPIO handling: common-obj-y += irq.o common-obj-y += hotplug.o +common-obj-y += iommu.o obj-y += nmi.o common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o diff --git a/hw/core/iommu.c b/hw/core/iommu.c new file mode 100644 index 0000000..e014e96 --- /dev/null +++ b/hw/core/iommu.c @@ -0,0 +1,61 @@ +/* + * QEMU emulation of IOMMU logic + * + * Copyright (C) 2017 Red Hat Inc. + * + * Authors: Peter Xu <pet...@redhat.com>, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "hw/core/iommu.h" + +IOMMUNotifier *iommu_notifier_register(IOMMUObject *iommu, + IOMMUNotifyFn fn, + uint64_t event_mask) +{ + IOMMUNotifier *notifier = g_new0(IOMMUNotifier, 1); + + assert((event_mask & ~IOMMU_EVENT_MASK) == 0); + notifier->event_mask = event_mask; + notifier->iommu_notify = fn; + QLIST_INSERT_HEAD(&iommu->iommu_notifiers, notifier, node); + + return notifier; +} + +void iommu_notifier_unregister(IOMMUObject *iommu, + IOMMUNotifier *notifier) +{ + IOMMUNotifier *cur, *next; + + QLIST_FOREACH_SAFE(cur, &iommu->iommu_notifiers, node, next) { + if (cur == notifier) { + QLIST_REMOVE(cur, node); + break; + } + } +} + +void iommu_notify(IOMMUObject *iommu, IOMMUEvent *event) +{ + IOMMUNotifier *cur; + + QLIST_FOREACH(cur, &iommu->iommu_notifiers, node) { + if (cur->event_mask & event->type && cur->iommu_notify) { + cur->iommu_notify(cur, event); + } + } +} diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 5131329..d6f6701 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2635,7 +2635,7 @@ static const MemoryRegionOps vtd_mem_ir_ops = { static IOMMUObject *vtd_as_iommu_get(AddressSpace *as) { VTDAddressSpace *vtd_dev_as = container_of(as, VTDAddressSpace, as); - return (IOMMUObject *)vtd_dev_as->iommu_state; + return &vtd_dev_as->iommu_state->iommu_common; } VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) diff --git a/include/exec/memory.h b/include/exec/memory.h index 0b0b58b..5ca1dd0 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -27,6 +27,7 @@ #include "qemu/notify.h" #include "qom/object.h" #include "qemu/rcu.h" +#include "hw/core/iommu.h" #define RAM_ADDR_INVALID (~(ram_addr_t)0) @@ -183,15 +184,6 @@ struct MemoryRegionOps { const MemoryRegionMmio old_mmio; }; -/* - * This stands for an IOMMU unit. Normally it should be exactly the - * IOMMU device, however this can also be actually anything which is - * related to that translation unit. What it is should be totally - * arch-dependent. Maybe one day we can have something better than a - * (void *) here. - */ -typedef void *IOMMUObject; - typedef struct MemoryRegionIOMMUOps MemoryRegionIOMMUOps; struct MemoryRegionIOMMUOps { diff --git a/include/hw/core/iommu.h b/include/hw/core/iommu.h new file mode 100644 index 0000000..16e6adf --- /dev/null +++ b/include/hw/core/iommu.h @@ -0,0 +1,72 @@ +/* + * QEMU emulation of IOMMU logic + * + * Copyright (C) 2017 Red Hat Inc. + * + * Authors: Peter Xu <pet...@redhat.com>, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __PCI_IOMMU_H__ +#define __PCI_IOMMU_H__ + +#include "qemu/queue.h" + +#define IOMMU_EVENT_SVM_PASID (0) +#define IOMMU_EVENT_MASK (IOMMU_EVENT_SVM_PASID) + +struct IOMMUEvent { + uint64_t type; + union { + struct { + /* TODO: fill in correct stuff. */ + int value; + } svm; + } data; +}; +typedef struct IOMMUEvent IOMMUEvent; + +typedef struct IOMMUNotifier IOMMUNotifier; + +typedef void (*IOMMUNotifyFn)(IOMMUNotifier *notifier, IOMMUEvent *event); + +struct IOMMUNotifier { + IOMMUNotifyFn iommu_notify; + /* + * What events we are listening to. Let's allow multiple event + * registrations from beginning. + */ + uint64_t event_mask; + QLIST_ENTRY(IOMMUNotifier) node; +}; + +/* + * This stands for an IOMMU unit. Any translation device should have + * this struct inside its own structure to make sure it can leverage + * common IOMMU functionalities. + */ +struct IOMMUObject { + QLIST_HEAD(, IOMMUNotifier) iommu_notifiers; +}; +typedef struct IOMMUObject IOMMUObject; + +IOMMUNotifier *iommu_notifier_register(IOMMUObject *iommu, + IOMMUNotifyFn fn, + uint64_t event_mask); +void iommu_notifier_unregister(IOMMUObject *iommu, + IOMMUNotifier *notifier); +void iommu_notify(IOMMUObject *iommu, IOMMUEvent *event); + +#endif diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index f9ac0ec..ec696f5 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -26,6 +26,7 @@ #include "hw/i386/x86-iommu.h" #include "hw/i386/ioapic.h" #include "hw/pci/msi.h" +#include "hw/core/iommu.h" #include "hw/sysbus.h" #define TYPE_INTEL_IOMMU_DEVICE "intel-iommu" @@ -258,6 +259,7 @@ struct IntelIOMMUMRNotifierNode { /* The iommu (DMAR) device state struct */ struct IntelIOMMUState { X86IOMMUState x86_iommu; + IOMMUObject iommu_common; MemoryRegion csrmem; uint8_t csr[DMAR_REG_SIZE]; /* register values */ uint8_t wmask[DMAR_REG_SIZE]; /* R/W bytes */ -- 2.7.4