On Thu, Apr 27, 2017 at 05:34:20PM +0800, Peter Xu wrote: > 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)
Peter, would it better to define it just like the IOTLB notifier flag? > +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); Regards to the IOMMUEvent definition, would it be helpful to use void*? virt-SVM may have two notifiers. They will have different data to pass in the notifier. Thanks, Yi L > +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 > >