1
0
mirror of https://gitlab.com/qemu-project/qemu synced 2024-06-29 06:14:38 +00:00

vfio queue:

* Add a host IOMMU device abstraction
 * VIRTIO-IOMMU/VFIO: Fix host iommu geometry handling
 * QOMify VFIOContainer
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmZ541QACgkQUaNDx8/7
 7KFdnQ/8Dih3HI2qtY93bTxg0lmJ+ZMibojTkEkTu3kSvwoI12wkiSMFKzzTWpZE
 UtGyIqQQij8IfQtIz87uQskv7oFiZKG6JWMTAX4uJ8ZIgZiih29/e/38VGEbogBh
 yO+1Pqr3ETlyLnQcu9ruBTJ293LXovmD4d9feoaVdURBNZ1EqIh7sv/y7YdUsR+i
 tXa6kW1ZIlKBI54o/uuODHWQYyOHs39VtZ6JZvgxVVEQsNikcJsosK9ts9A1EByi
 0roQVXm2QAK/nPXlmMGLvJWzQcdeXQ6W6hzYkO2HqGnCLURnpW+y/ZVbNcxGOOiU
 2G6L0TASlqA3yqCJeLuZZqjM6S2VbnvrA8omyg4QnygIHppYjp2CdcCmUpg6wfze
 rkgbVLNasX+le4ss2emuHPh55dLDP20yW83DeGeqSgE//foaJWhtOK/cnvs04zV2
 D6oSAVsOsZ6ozYlQckYnaxIBANDKLRnzCXVZLUCmHxCUhxHuiNJUsHfZYIv/Zxen
 C5ZjD/JPgx3onkoKbNfTRTgwOCdXhVPjWnnp7Su49jymsekqdk1ntln4ixDT3Vol
 ghQPQLjICBc8qXiOJAcFDwqLf/telPlzUUzvlDeC4BYMnpBAP6rQ3JJ8i0vCCiWv
 zKCtmbcDqDRMDpWyJWM3XA/kVKP9i2tNa1R/ej2SleCFLgRapBw=
 =3koe
 -----END PGP SIGNATURE-----

Merge tag 'pull-vfio-20240624' of https://github.com/legoater/qemu into staging

vfio queue:

* Add a host IOMMU device abstraction
* VIRTIO-IOMMU/VFIO: Fix host iommu geometry handling
* QOMify VFIOContainer

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmZ541QACgkQUaNDx8/7
# 7KFdnQ/8Dih3HI2qtY93bTxg0lmJ+ZMibojTkEkTu3kSvwoI12wkiSMFKzzTWpZE
# UtGyIqQQij8IfQtIz87uQskv7oFiZKG6JWMTAX4uJ8ZIgZiih29/e/38VGEbogBh
# yO+1Pqr3ETlyLnQcu9ruBTJ293LXovmD4d9feoaVdURBNZ1EqIh7sv/y7YdUsR+i
# tXa6kW1ZIlKBI54o/uuODHWQYyOHs39VtZ6JZvgxVVEQsNikcJsosK9ts9A1EByi
# 0roQVXm2QAK/nPXlmMGLvJWzQcdeXQ6W6hzYkO2HqGnCLURnpW+y/ZVbNcxGOOiU
# 2G6L0TASlqA3yqCJeLuZZqjM6S2VbnvrA8omyg4QnygIHppYjp2CdcCmUpg6wfze
# rkgbVLNasX+le4ss2emuHPh55dLDP20yW83DeGeqSgE//foaJWhtOK/cnvs04zV2
# D6oSAVsOsZ6ozYlQckYnaxIBANDKLRnzCXVZLUCmHxCUhxHuiNJUsHfZYIv/Zxen
# C5ZjD/JPgx3onkoKbNfTRTgwOCdXhVPjWnnp7Su49jymsekqdk1ntln4ixDT3Vol
# ghQPQLjICBc8qXiOJAcFDwqLf/telPlzUUzvlDeC4BYMnpBAP6rQ3JJ8i0vCCiWv
# zKCtmbcDqDRMDpWyJWM3XA/kVKP9i2tNa1R/ej2SleCFLgRapBw=
# =3koe
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 24 Jun 2024 02:21:24 PM PDT
# gpg:                using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1
# gpg: Good signature from "Cédric Le Goater <clg@kaod.org>" [undefined]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: A0F6 6548 F048 95EB FE6B  0B60 51A3 43C7 CFFB ECA1

* tag 'pull-vfio-20240624' of https://github.com/legoater/qemu: (42 commits)
  vfio/container: Move vfio_container_destroy() to an instance_finalize() handler
  vfio/container: Introduce vfio_iommu_legacy_instance_init()
  vfio/container: Remove vfio_container_init()
  vfio/container: Remove VFIOContainerBase::ops
  vfio/container: Introduce an instance_init() handler
  vfio/container: Switch to QOM
  vfio/container: Change VFIOContainerBase to use QOM
  vfio/container: Discover IOMMU type before creating the container
  vfio/container: Introduce vfio_create_container()
  vfio/container: Introduce vfio_get_iommu_class_name()
  vfio/container: Modify vfio_get_iommu_type() to use a container fd
  vfio/container: Simplify vfio_container_init()
  vfio/container: Introduce vfio_address_space_insert()
  vfio/common: Extract vIOMMU code from vfio_sync_dirty_bitmap()
  vfio/common: Move dirty tracking ranges update to helper
  vfio: Remove unused declarations from vfio-common.h
  vfio: Make vfio_devices_dma_logging_start() return bool
  memory: Remove IOMMU MR iommu_set_iova_range API
  hw/vfio: Remove memory_region_iommu_set_iova_ranges() call
  virtio-iommu: Remove the implementation of iommu_set_iova_range
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2024-06-24 21:30:34 -07:00
commit 3f044554b9
24 changed files with 1068 additions and 380 deletions

View File

@ -2198,6 +2198,8 @@ M: Zhenzhong Duan <zhenzhong.duan@intel.com>
S: Supported
F: backends/iommufd.c
F: include/sysemu/iommufd.h
F: backends/host_iommu_device.c
F: include/sysemu/host_iommu_device.h
F: include/qemu/chardev_open.h
F: util/chardev_open.c
F: docs/devel/vfio-iommufd.rst

View File

@ -0,0 +1,33 @@
/*
* Host IOMMU device abstract
*
* Copyright (C) 2024 Intel Corporation.
*
* Authors: Zhenzhong Duan <zhenzhong.duan@intel.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "sysemu/host_iommu_device.h"
OBJECT_DEFINE_ABSTRACT_TYPE(HostIOMMUDevice,
host_iommu_device,
HOST_IOMMU_DEVICE,
OBJECT)
static void host_iommu_device_class_init(ObjectClass *oc, void *data)
{
}
static void host_iommu_device_init(Object *obj)
{
}
static void host_iommu_device_finalize(Object *obj)
{
HostIOMMUDevice *hiod = HOST_IOMMU_DEVICE(obj);
g_free(hiod->name);
}

View File

@ -208,23 +208,69 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
return ret;
}
static const TypeInfo iommufd_backend_info = {
.name = TYPE_IOMMUFD_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(IOMMUFDBackend),
.instance_init = iommufd_backend_init,
.instance_finalize = iommufd_backend_finalize,
.class_size = sizeof(IOMMUFDBackendClass),
.class_init = iommufd_backend_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid,
uint32_t *type, void *data, uint32_t len,
Error **errp)
{
struct iommu_hw_info info = {
.size = sizeof(info),
.dev_id = devid,
.data_len = len,
.data_uptr = (uintptr_t)data,
};
if (ioctl(be->fd, IOMMU_GET_HW_INFO, &info)) {
error_setg_errno(errp, errno, "Failed to get hardware info");
return false;
}
g_assert(type);
*type = info.out_data_type;
return true;
}
static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp)
{
HostIOMMUDeviceCaps *caps = &hiod->caps;
switch (cap) {
case HOST_IOMMU_DEVICE_CAP_IOMMU_TYPE:
return caps->type;
case HOST_IOMMU_DEVICE_CAP_AW_BITS:
return caps->aw_bits;
default:
error_setg(errp, "%s: unsupported capability %x", hiod->name, cap);
return -EINVAL;
}
}
static void hiod_iommufd_class_init(ObjectClass *oc, void *data)
{
HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc);
hioc->get_cap = hiod_iommufd_get_cap;
};
static const TypeInfo types[] = {
{
.name = TYPE_IOMMUFD_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(IOMMUFDBackend),
.instance_init = iommufd_backend_init,
.instance_finalize = iommufd_backend_finalize,
.class_size = sizeof(IOMMUFDBackendClass),
.class_init = iommufd_backend_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
}, {
.name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD,
.parent = TYPE_HOST_IOMMU_DEVICE,
.class_init = hiod_iommufd_class_init,
.abstract = true,
}
};
static void register_types(void)
{
type_register_static(&iommufd_backend_info);
}
type_init(register_types);
DEFINE_TYPES(types)

View File

@ -16,6 +16,7 @@ if host_os != 'windows'
endif
if host_os == 'linux'
system_ss.add(files('hostmem-memfd.c'))
system_ss.add(files('host_iommu_device.c'))
endif
if keyutils.found()
system_ss.add(keyutils, files('cryptodev-lkcf.c'))

View File

@ -61,6 +61,12 @@ struct vtd_as_key {
uint32_t pasid;
};
/* bus/devfn is PCI device's real BDF not the aliased one */
struct vtd_hiod_key {
PCIBus *bus;
uint8_t devfn;
};
struct vtd_iotlb_key {
uint64_t gfn;
uint32_t pasid;
@ -250,6 +256,25 @@ static guint vtd_as_hash(gconstpointer v)
return (guint)(value << 8 | key->devfn);
}
/* Same implementation as vtd_as_hash() */
static guint vtd_hiod_hash(gconstpointer v)
{
return vtd_as_hash(v);
}
static gboolean vtd_hiod_equal(gconstpointer v1, gconstpointer v2)
{
const struct vtd_hiod_key *key1 = v1;
const struct vtd_hiod_key *key2 = v2;
return (key1->bus == key2->bus) && (key1->devfn == key2->devfn);
}
static void vtd_hiod_destroy(gpointer v)
{
object_unref(v);
}
static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value,
gpointer user_data)
{
@ -3812,6 +3837,87 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus,
return vtd_dev_as;
}
static bool vtd_check_hiod(IntelIOMMUState *s, HostIOMMUDevice *hiod,
Error **errp)
{
HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod);
int ret;
if (!hiodc->get_cap) {
error_setg(errp, ".get_cap() not implemented");
return false;
}
/* Common checks */
ret = hiodc->get_cap(hiod, HOST_IOMMU_DEVICE_CAP_AW_BITS, errp);
if (ret < 0) {
return false;
}
if (s->aw_bits > ret) {
error_setg(errp, "aw-bits %d > host aw-bits %d", s->aw_bits, ret);
return false;
}
return true;
}
static bool vtd_dev_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
HostIOMMUDevice *hiod, Error **errp)
{
IntelIOMMUState *s = opaque;
struct vtd_as_key key = {
.bus = bus,
.devfn = devfn,
};
struct vtd_as_key *new_key;
assert(hiod);
vtd_iommu_lock(s);
if (g_hash_table_lookup(s->vtd_host_iommu_dev, &key)) {
error_setg(errp, "Host IOMMU device already exist");
vtd_iommu_unlock(s);
return false;
}
if (!vtd_check_hiod(s, hiod, errp)) {
vtd_iommu_unlock(s);
return false;
}
new_key = g_malloc(sizeof(*new_key));
new_key->bus = bus;
new_key->devfn = devfn;
object_ref(hiod);
g_hash_table_insert(s->vtd_host_iommu_dev, new_key, hiod);
vtd_iommu_unlock(s);
return true;
}
static void vtd_dev_unset_iommu_device(PCIBus *bus, void *opaque, int devfn)
{
IntelIOMMUState *s = opaque;
struct vtd_as_key key = {
.bus = bus,
.devfn = devfn,
};
vtd_iommu_lock(s);
if (!g_hash_table_lookup(s->vtd_host_iommu_dev, &key)) {
vtd_iommu_unlock(s);
return;
}
g_hash_table_remove(s->vtd_host_iommu_dev, &key);
vtd_iommu_unlock(s);
}
/* Unmap the whole range in the notifier's scope. */
static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n)
{
@ -3934,30 +4040,10 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n)
return;
}
/* Do the initialization. It will also be called when reset, so pay
* attention when adding new initialization stuff.
*/
static void vtd_init(IntelIOMMUState *s)
static void vtd_cap_init(IntelIOMMUState *s)
{
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
memset(s->csr, 0, DMAR_REG_SIZE);
memset(s->wmask, 0, DMAR_REG_SIZE);
memset(s->w1cmask, 0, DMAR_REG_SIZE);
memset(s->womask, 0, DMAR_REG_SIZE);
s->root = 0;
s->root_scalable = false;
s->dmar_enabled = false;
s->intr_enabled = false;
s->iq_head = 0;
s->iq_tail = 0;
s->iq = 0;
s->iq_size = 0;
s->qi_enabled = false;
s->iq_last_desc_type = VTD_INV_DESC_NONE;
s->iq_dw = false;
s->next_frcd_reg = 0;
s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND |
VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS |
VTD_CAP_MGAW(s->aw_bits);
@ -3974,27 +4060,6 @@ static void vtd_init(IntelIOMMUState *s)
}
s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO;
/*
* Rsvd field masks for spte
*/
vtd_spte_rsvd[0] = ~0ULL;
vtd_spte_rsvd[1] = VTD_SPTE_PAGE_L1_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
vtd_spte_rsvd[2] = VTD_SPTE_PAGE_L2_RSVD_MASK(s->aw_bits);
vtd_spte_rsvd[3] = VTD_SPTE_PAGE_L3_RSVD_MASK(s->aw_bits);
vtd_spte_rsvd[4] = VTD_SPTE_PAGE_L4_RSVD_MASK(s->aw_bits);
vtd_spte_rsvd_large[2] = VTD_SPTE_LPAGE_L2_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
if (s->scalable_mode || s->snoop_control) {
vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP;
vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP;
vtd_spte_rsvd_large[3] &= ~VTD_SPTE_SNP;
}
if (x86_iommu_ir_supported(x86_iommu)) {
s->ecap |= VTD_ECAP_IR | VTD_ECAP_MHMV;
if (s->intr_eim == ON_OFF_AUTO_ON) {
@ -4027,6 +4092,56 @@ static void vtd_init(IntelIOMMUState *s)
if (s->pasid) {
s->ecap |= VTD_ECAP_PASID;
}
}
/*
* Do the initialization. It will also be called when reset, so pay
* attention when adding new initialization stuff.
*/
static void vtd_init(IntelIOMMUState *s)
{
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
memset(s->csr, 0, DMAR_REG_SIZE);
memset(s->wmask, 0, DMAR_REG_SIZE);
memset(s->w1cmask, 0, DMAR_REG_SIZE);
memset(s->womask, 0, DMAR_REG_SIZE);
s->root = 0;
s->root_scalable = false;
s->dmar_enabled = false;
s->intr_enabled = false;
s->iq_head = 0;
s->iq_tail = 0;
s->iq = 0;
s->iq_size = 0;
s->qi_enabled = false;
s->iq_last_desc_type = VTD_INV_DESC_NONE;
s->iq_dw = false;
s->next_frcd_reg = 0;
vtd_cap_init(s);
/*
* Rsvd field masks for spte
*/
vtd_spte_rsvd[0] = ~0ULL;
vtd_spte_rsvd[1] = VTD_SPTE_PAGE_L1_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
vtd_spte_rsvd[2] = VTD_SPTE_PAGE_L2_RSVD_MASK(s->aw_bits);
vtd_spte_rsvd[3] = VTD_SPTE_PAGE_L3_RSVD_MASK(s->aw_bits);
vtd_spte_rsvd[4] = VTD_SPTE_PAGE_L4_RSVD_MASK(s->aw_bits);
vtd_spte_rsvd_large[2] = VTD_SPTE_LPAGE_L2_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
if (s->scalable_mode || s->snoop_control) {
vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP;
vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP;
vtd_spte_rsvd_large[3] &= ~VTD_SPTE_SNP;
}
vtd_reset_caches(s);
@ -4107,6 +4222,8 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
static PCIIOMMUOps vtd_iommu_ops = {
.get_address_space = vtd_host_dma_iommu,
.set_iommu_device = vtd_dev_set_iommu_device,
.unset_iommu_device = vtd_dev_unset_iommu_device,
};
static bool vtd_decide_config(IntelIOMMUState *s, Error **errp)
@ -4226,6 +4343,8 @@ static void vtd_realize(DeviceState *dev, Error **errp)
g_free, g_free);
s->vtd_address_spaces = g_hash_table_new_full(vtd_as_hash, vtd_as_equal,
g_free, g_free);
s->vtd_host_iommu_dev = g_hash_table_new_full(vtd_hiod_hash, vtd_hiod_equal,
g_free, vtd_hiod_destroy);
vtd_init(s);
pci_setup_iommu(bus, &vtd_iommu_ops, dev);
/* Pseudo address space under root PCI bus. */

View File

@ -2648,11 +2648,27 @@ static void pci_device_class_base_init(ObjectClass *klass, void *data)
}
}
AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
/*
* Get IOMMU root bus, aliased bus and devfn of a PCI device
*
* IOMMU root bus is needed by all call sites to call into iommu_ops.
* For call sites which don't need aliased BDF, passing NULL to
* aliased_[bus|devfn] is allowed.
*
* @piommu_bus: return root #PCIBus backed by an IOMMU for the PCI device.
*
* @aliased_bus: return aliased #PCIBus of the PCI device, optional.
*
* @aliased_devfn: return aliased devfn of the PCI device, optional.
*/
static void pci_device_get_iommu_bus_devfn(PCIDevice *dev,
PCIBus **piommu_bus,
PCIBus **aliased_bus,
int *aliased_devfn)
{
PCIBus *bus = pci_get_bus(dev);
PCIBus *iommu_bus = bus;
uint8_t devfn = dev->devfn;
int devfn = dev->devfn;
while (iommu_bus && !iommu_bus->iommu_ops && iommu_bus->parent_dev) {
PCIBus *parent_bus = pci_get_bus(iommu_bus->parent_dev);
@ -2693,13 +2709,70 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
iommu_bus = parent_bus;
}
if (!pci_bus_bypass_iommu(bus) && iommu_bus->iommu_ops) {
assert(0 <= devfn && devfn < PCI_DEVFN_MAX);
assert(iommu_bus);
if (pci_bus_bypass_iommu(bus) || !iommu_bus->iommu_ops) {
iommu_bus = NULL;
}
*piommu_bus = iommu_bus;
if (aliased_bus) {
*aliased_bus = bus;
}
if (aliased_devfn) {
*aliased_devfn = devfn;
}
}
AddressSpace *pci_device_iommu_address_space(PCIDevice *dev)
{
PCIBus *bus;
PCIBus *iommu_bus;
int devfn;
pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn);
if (iommu_bus) {
return iommu_bus->iommu_ops->get_address_space(bus,
iommu_bus->iommu_opaque, devfn);
}
return &address_space_memory;
}
bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod,
Error **errp)
{
PCIBus *iommu_bus, *aliased_bus;
int aliased_devfn;
/* set_iommu_device requires device's direct BDF instead of aliased BDF */
pci_device_get_iommu_bus_devfn(dev, &iommu_bus,
&aliased_bus, &aliased_devfn);
if (iommu_bus && iommu_bus->iommu_ops->set_iommu_device) {
hiod->aliased_bus = aliased_bus;
hiod->aliased_devfn = aliased_devfn;
return iommu_bus->iommu_ops->set_iommu_device(pci_get_bus(dev),
iommu_bus->iommu_opaque,
dev->devfn, hiod, errp);
}
return true;
}
void pci_device_unset_iommu_device(PCIDevice *dev)
{
PCIBus *iommu_bus;
pci_device_get_iommu_bus_devfn(dev, &iommu_bus, NULL, NULL);
if (iommu_bus && iommu_bus->iommu_ops->unset_iommu_device) {
return iommu_bus->iommu_ops->unset_iommu_device(pci_get_bus(dev),
iommu_bus->iommu_opaque,
dev->devfn);
}
}
void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque)
{
/*

View File

@ -630,16 +630,6 @@ static void vfio_listener_region_add(MemoryListener *listener,
goto fail;
}
if (bcontainer->iova_ranges) {
ret = memory_region_iommu_set_iova_ranges(giommu->iommu_mr,
bcontainer->iova_ranges,
&err);
if (ret) {
g_free(giommu);
goto fail;
}
}
ret = memory_region_register_iommu_notifier(section->mr, &giommu->n,
&err);
if (ret) {
@ -849,20 +839,11 @@ static bool vfio_section_is_vfio_pci(MemoryRegionSection *section,
return false;
}
static void vfio_dirty_tracking_update(MemoryListener *listener,
MemoryRegionSection *section)
static void vfio_dirty_tracking_update_range(VFIODirtyRanges *range,
hwaddr iova, hwaddr end,
bool update_pci)
{
VFIODirtyRangesListener *dirty = container_of(listener,
VFIODirtyRangesListener,
listener);
VFIODirtyRanges *range = &dirty->ranges;
hwaddr iova, end, *min, *max;
if (!vfio_listener_valid_section(section, "tracking_update") ||
!vfio_get_section_iova_range(dirty->bcontainer, section,
&iova, &end, NULL)) {
return;
}
hwaddr *min, *max;
/*
* The address space passed to the dirty tracker is reduced to three ranges:
@ -883,8 +864,7 @@ static void vfio_dirty_tracking_update(MemoryListener *listener,
* The alternative would be an IOVATree but that has a much bigger runtime
* overhead and unnecessary complexity.
*/
if (vfio_section_is_vfio_pci(section, dirty->bcontainer) &&
iova >= UINT32_MAX) {
if (update_pci && iova >= UINT32_MAX) {
min = &range->minpci64;
max = &range->maxpci64;
} else {
@ -899,7 +879,23 @@ static void vfio_dirty_tracking_update(MemoryListener *listener,
}
trace_vfio_device_dirty_tracking_update(iova, end, *min, *max);
return;
}
static void vfio_dirty_tracking_update(MemoryListener *listener,
MemoryRegionSection *section)
{
VFIODirtyRangesListener *dirty =
container_of(listener, VFIODirtyRangesListener, listener);
hwaddr iova, end;
if (!vfio_listener_valid_section(section, "tracking_update") ||
!vfio_get_section_iova_range(dirty->bcontainer, section,
&iova, &end, NULL)) {
return;
}
vfio_dirty_tracking_update_range(&dirty->ranges, iova, end,
vfio_section_is_vfio_pci(section, dirty->bcontainer));
}
static const MemoryListener vfio_dirty_tracking_listener = {
@ -1030,7 +1026,7 @@ static void vfio_device_feature_dma_logging_start_destroy(
g_free(feature);
}
static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer,
static bool vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer,
Error **errp)
{
struct vfio_device_feature *feature;
@ -1043,7 +1039,7 @@ static int vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer,
&ranges);
if (!feature) {
error_setg_errno(errp, errno, "Failed to prepare DMA logging");
return -errno;
return false;
}
QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) {
@ -1068,7 +1064,7 @@ out:
vfio_device_feature_dma_logging_start_destroy(feature);
return ret;
return ret == 0;
}
static bool vfio_listener_log_global_start(MemoryListener *listener,
@ -1077,18 +1073,18 @@ static bool vfio_listener_log_global_start(MemoryListener *listener,
ERRP_GUARD();
VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase,
listener);
int ret;
bool ret;
if (vfio_devices_all_device_dirty_tracking(bcontainer)) {
ret = vfio_devices_dma_logging_start(bcontainer, errp);
} else {
ret = vfio_container_set_dirty_page_tracking(bcontainer, true, errp);
ret = vfio_container_set_dirty_page_tracking(bcontainer, true, errp) == 0;
}
if (ret) {
if (!ret) {
error_prepend(errp, "vfio: Could not start dirty page tracking - ");
}
return !ret;
return ret;
}
static void vfio_listener_log_global_stop(MemoryListener *listener)
@ -1306,37 +1302,50 @@ vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer,
&vrdl);
}
static int vfio_sync_iommu_dirty_bitmap(VFIOContainerBase *bcontainer,
MemoryRegionSection *section)
{
VFIOGuestIOMMU *giommu;
bool found = false;
Int128 llend;
vfio_giommu_dirty_notifier gdn;
int idx;
QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) {
if (MEMORY_REGION(giommu->iommu_mr) == section->mr &&
giommu->n.start == section->offset_within_region) {
found = true;
break;
}
}
if (!found) {
return 0;
}
gdn.giommu = giommu;
idx = memory_region_iommu_attrs_to_index(giommu->iommu_mr,
MEMTXATTRS_UNSPECIFIED);
llend = int128_add(int128_make64(section->offset_within_region),
section->size);
llend = int128_sub(llend, int128_one());
iommu_notifier_init(&gdn.n, vfio_iommu_map_dirty_notify, IOMMU_NOTIFIER_MAP,
section->offset_within_region, int128_get64(llend),
idx);
memory_region_iommu_replay(giommu->iommu_mr, &gdn.n);
return 0;
}
static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer,
MemoryRegionSection *section, Error **errp)
{
ram_addr_t ram_addr;
if (memory_region_is_iommu(section->mr)) {
VFIOGuestIOMMU *giommu;
QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) {
if (MEMORY_REGION(giommu->iommu_mr) == section->mr &&
giommu->n.start == section->offset_within_region) {
Int128 llend;
vfio_giommu_dirty_notifier gdn = { .giommu = giommu };
int idx = memory_region_iommu_attrs_to_index(giommu->iommu_mr,
MEMTXATTRS_UNSPECIFIED);
llend = int128_add(int128_make64(section->offset_within_region),
section->size);
llend = int128_sub(llend, int128_one());
iommu_notifier_init(&gdn.n,
vfio_iommu_map_dirty_notify,
IOMMU_NOTIFIER_MAP,
section->offset_within_region,
int128_get64(llend),
idx);
memory_region_iommu_replay(giommu->iommu_mr, &gdn.n);
break;
}
}
return 0;
return vfio_sync_iommu_dirty_bitmap(bcontainer, section);
} else if (memory_region_has_ram_discard_manager(section->mr)) {
int ret;
@ -1499,6 +1508,13 @@ void vfio_put_address_space(VFIOAddressSpace *space)
}
}
void vfio_address_space_insert(VFIOAddressSpace *space,
VFIOContainerBase *bcontainer)
{
QLIST_INSERT_HEAD(&space->containers, bcontainer, next);
bcontainer->space = space;
}
struct vfio_device_info *vfio_get_device_info(int fd)
{
struct vfio_device_info *info;
@ -1528,6 +1544,7 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev,
{
const VFIOIOMMUClass *ops =
VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY));
HostIOMMUDevice *hiod;
if (vbasedev->iommufd) {
ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD));
@ -1535,7 +1552,19 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev,
assert(ops);
return ops->attach_device(name, vbasedev, as, errp);
if (!ops->attach_device(name, vbasedev, as, errp)) {
return false;
}
hiod = HOST_IOMMU_DEVICE(object_new(ops->hiod_typename));
if (!HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp)) {
object_unref(hiod);
ops->detach_device(vbasedev);
return false;
}
vbasedev->hiod = hiod;
return true;
}
void vfio_detach_device(VFIODevice *vbasedev)
@ -1543,5 +1572,6 @@ void vfio_detach_device(VFIODevice *vbasedev)
if (!vbasedev->bcontainer) {
return;
}
vbasedev->bcontainer->ops->detach_device(vbasedev);
object_unref(vbasedev->hiod);
VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer)->detach_device(vbasedev);
}

View File

@ -19,73 +19,73 @@ int vfio_container_dma_map(VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
void *vaddr, bool readonly)
{
g_assert(bcontainer->ops->dma_map);
return bcontainer->ops->dma_map(bcontainer, iova, size, vaddr, readonly);
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
g_assert(vioc->dma_map);
return vioc->dma_map(bcontainer, iova, size, vaddr, readonly);
}
int vfio_container_dma_unmap(VFIOContainerBase *bcontainer,
hwaddr iova, ram_addr_t size,
IOMMUTLBEntry *iotlb)
{
g_assert(bcontainer->ops->dma_unmap);
return bcontainer->ops->dma_unmap(bcontainer, iova, size, iotlb);
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
g_assert(vioc->dma_unmap);
return vioc->dma_unmap(bcontainer, iova, size, iotlb);
}
bool vfio_container_add_section_window(VFIOContainerBase *bcontainer,
MemoryRegionSection *section,
Error **errp)
{
if (!bcontainer->ops->add_window) {
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
if (!vioc->add_window) {
return true;
}
return bcontainer->ops->add_window(bcontainer, section, errp);
return vioc->add_window(bcontainer, section, errp);
}
void vfio_container_del_section_window(VFIOContainerBase *bcontainer,
MemoryRegionSection *section)
{
if (!bcontainer->ops->del_window) {
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
if (!vioc->del_window) {
return;
}
return bcontainer->ops->del_window(bcontainer, section);
return vioc->del_window(bcontainer, section);
}
int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer,
bool start, Error **errp)
{
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
if (!bcontainer->dirty_pages_supported) {
return 0;
}
g_assert(bcontainer->ops->set_dirty_page_tracking);
return bcontainer->ops->set_dirty_page_tracking(bcontainer, start, errp);
g_assert(vioc->set_dirty_page_tracking);
return vioc->set_dirty_page_tracking(bcontainer, start, errp);
}
int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp)
{
g_assert(bcontainer->ops->query_dirty_bitmap);
return bcontainer->ops->query_dirty_bitmap(bcontainer, vbmap, iova, size,
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
g_assert(vioc->query_dirty_bitmap);
return vioc->query_dirty_bitmap(bcontainer, vbmap, iova, size,
errp);
}
void vfio_container_init(VFIOContainerBase *bcontainer, VFIOAddressSpace *space,
const VFIOIOMMUClass *ops)
{
bcontainer->ops = ops;
bcontainer->space = space;
bcontainer->error = NULL;
bcontainer->dirty_pages_supported = false;
bcontainer->dma_max_mappings = 0;
bcontainer->iova_ranges = NULL;
QLIST_INIT(&bcontainer->giommu_list);
QLIST_INIT(&bcontainer->vrdl_list);
}
void vfio_container_destroy(VFIOContainerBase *bcontainer)
static void vfio_container_instance_finalize(Object *obj)
{
VFIOContainerBase *bcontainer = VFIO_IOMMU(obj);
VFIOGuestIOMMU *giommu, *tmp;
QLIST_REMOVE(bcontainer, next);
@ -100,11 +100,27 @@ void vfio_container_destroy(VFIOContainerBase *bcontainer)
g_list_free_full(bcontainer->iova_ranges, g_free);
}
static void vfio_container_instance_init(Object *obj)
{
VFIOContainerBase *bcontainer = VFIO_IOMMU(obj);
bcontainer->error = NULL;
bcontainer->dirty_pages_supported = false;
bcontainer->dma_max_mappings = 0;
bcontainer->iova_ranges = NULL;
QLIST_INIT(&bcontainer->giommu_list);
QLIST_INIT(&bcontainer->vrdl_list);
}
static const TypeInfo types[] = {
{
.name = TYPE_VFIO_IOMMU,
.parent = TYPE_INTERFACE,
.parent = TYPE_OBJECT,
.instance_init = vfio_container_instance_init,
.instance_finalize = vfio_container_instance_finalize,
.instance_size = sizeof(VFIOContainerBase),
.class_size = sizeof(VFIOIOMMUClass),
.abstract = true,
},
};

View File

@ -354,7 +354,7 @@ static void vfio_kvm_device_del_group(VFIOGroup *group)
/*
* vfio_get_iommu_type - selects the richest iommu_type (v2 first)
*/
static int vfio_get_iommu_type(VFIOContainer *container,
static int vfio_get_iommu_type(int container_fd,
Error **errp)
{
int iommu_types[] = { VFIO_TYPE1v2_IOMMU, VFIO_TYPE1_IOMMU,
@ -362,7 +362,7 @@ static int vfio_get_iommu_type(VFIOContainer *container,
int i;
for (i = 0; i < ARRAY_SIZE(iommu_types); i++) {
if (ioctl(container->fd, VFIO_CHECK_EXTENSION, iommu_types[i])) {
if (ioctl(container_fd, VFIO_CHECK_EXTENSION, iommu_types[i])) {
return iommu_types[i];
}
}
@ -373,67 +373,70 @@ static int vfio_get_iommu_type(VFIOContainer *container,
/*
* vfio_get_iommu_ops - get a VFIOIOMMUClass associated with a type
*/
static const VFIOIOMMUClass *vfio_get_iommu_class(int iommu_type, Error **errp)
static const char *vfio_get_iommu_class_name(int iommu_type)
{
ObjectClass *klass = NULL;
switch (iommu_type) {
case VFIO_TYPE1v2_IOMMU:
case VFIO_TYPE1_IOMMU:
klass = object_class_by_name(TYPE_VFIO_IOMMU_LEGACY);
return TYPE_VFIO_IOMMU_LEGACY;
break;
case VFIO_SPAPR_TCE_v2_IOMMU:
case VFIO_SPAPR_TCE_IOMMU:
klass = object_class_by_name(TYPE_VFIO_IOMMU_SPAPR);
return TYPE_VFIO_IOMMU_SPAPR;
break;
default:
g_assert_not_reached();
};
return VFIO_IOMMU_CLASS(klass);
}
static bool vfio_set_iommu(VFIOContainer *container, int group_fd,
VFIOAddressSpace *space, Error **errp)
static bool vfio_set_iommu(int container_fd, int group_fd,
int *iommu_type, Error **errp)
{
int iommu_type;
const VFIOIOMMUClass *vioc;
iommu_type = vfio_get_iommu_type(container, errp);
if (iommu_type < 0) {
return false;
}
if (ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
if (ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container_fd)) {
error_setg_errno(errp, errno, "Failed to set group container");
return false;
}
while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) {
if (iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
while (ioctl(container_fd, VFIO_SET_IOMMU, *iommu_type)) {
if (*iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) {
/*
* On sPAPR, despite the IOMMU subdriver always advertises v1 and
* v2, the running platform may not support v2 and there is no
* way to guess it until an IOMMU group gets added to the container.
* So in case it fails with v2, try v1 as a fallback.
*/
iommu_type = VFIO_SPAPR_TCE_IOMMU;
*iommu_type = VFIO_SPAPR_TCE_IOMMU;
continue;
}
error_setg_errno(errp, errno, "Failed to set iommu for container");
return false;
}
container->iommu_type = iommu_type;
return true;
}
vioc = vfio_get_iommu_class(iommu_type, errp);
if (!vioc) {
error_setg(errp, "No available IOMMU models");
return false;
static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group,
Error **errp)
{
int iommu_type;
const char *vioc_name;
VFIOContainer *container;
iommu_type = vfio_get_iommu_type(fd, errp);
if (iommu_type < 0) {
return NULL;
}
vfio_container_init(&container->bcontainer, space, vioc);
return true;
if (!vfio_set_iommu(fd, group->fd, &iommu_type, errp)) {
return NULL;
}
vioc_name = vfio_get_iommu_class_name(iommu_type);
container = VFIO_IOMMU_LEGACY(object_new(vioc_name));
container->fd = fd;
container->iommu_type = iommu_type;
return container;
}
static int vfio_get_iommu_info(VFIOContainer *container,
@ -542,6 +545,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as,
VFIOContainerBase *bcontainer;
int ret, fd;
VFIOAddressSpace *space;
VFIOIOMMUClass *vioc;
space = vfio_get_address_space(as);
@ -610,13 +614,11 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as,
goto close_fd_exit;
}
container = g_malloc0(sizeof(*container));
container->fd = fd;
bcontainer = &container->bcontainer;
if (!vfio_set_iommu(container, group->fd, space, errp)) {
goto free_container_exit;
container = vfio_create_container(fd, group, errp);
if (!container) {
goto close_fd_exit;
}
bcontainer = &container->bcontainer;
if (!vfio_cpr_register_container(bcontainer, errp)) {
goto free_container_exit;
@ -628,16 +630,16 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as,
goto unregister_container_exit;
}
assert(bcontainer->ops->setup);
vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
assert(vioc->setup);
if (!bcontainer->ops->setup(bcontainer, errp)) {
if (!vioc->setup(bcontainer, errp)) {
goto enable_discards_exit;
}
vfio_kvm_device_add_group(group);
QLIST_INIT(&container->group_list);
QLIST_INSERT_HEAD(&space->containers, bcontainer, next);
vfio_address_space_insert(space, bcontainer);
group->container = container;
QLIST_INSERT_HEAD(&container->group_list, group, container_next);
@ -659,8 +661,8 @@ listener_release_exit:
QLIST_REMOVE(bcontainer, next);
vfio_kvm_device_del_group(group);
memory_listener_unregister(&bcontainer->listener);
if (bcontainer->ops->release) {
bcontainer->ops->release(bcontainer);
if (vioc->release) {
vioc->release(bcontainer);
}
enable_discards_exit:
@ -670,7 +672,7 @@ unregister_container_exit:
vfio_cpr_unregister_container(bcontainer);
free_container_exit:
g_free(container);
object_unref(container);
close_fd_exit:
close(fd);
@ -685,6 +687,7 @@ static void vfio_disconnect_container(VFIOGroup *group)
{
VFIOContainer *container = group->container;
VFIOContainerBase *bcontainer = &container->bcontainer;
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
QLIST_REMOVE(group, container_next);
group->container = NULL;
@ -696,8 +699,8 @@ static void vfio_disconnect_container(VFIOGroup *group)
*/
if (QLIST_EMPTY(&container->group_list)) {
memory_listener_unregister(&bcontainer->listener);
if (bcontainer->ops->release) {
bcontainer->ops->release(bcontainer);
if (vioc->release) {
vioc->release(bcontainer);
}
}
@ -709,12 +712,10 @@ static void vfio_disconnect_container(VFIOGroup *group)
if (QLIST_EMPTY(&container->group_list)) {
VFIOAddressSpace *space = bcontainer->space;
vfio_container_destroy(bcontainer);
trace_vfio_disconnect_container(container->fd);
vfio_cpr_unregister_container(bcontainer);
close(container->fd);
g_free(container);
object_unref(container);
vfio_put_address_space(space);
}
@ -1126,6 +1127,8 @@ static void vfio_iommu_legacy_class_init(ObjectClass *klass, void *data)
{
VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass);
vioc->hiod_typename = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO;
vioc->setup = vfio_legacy_setup;
vioc->dma_map = vfio_legacy_dma_map;
vioc->dma_unmap = vfio_legacy_dma_unmap;
@ -1136,12 +1139,75 @@ static void vfio_iommu_legacy_class_init(ObjectClass *klass, void *data)
vioc->pci_hot_reset = vfio_legacy_pci_hot_reset;
};
static bool hiod_legacy_vfio_realize(HostIOMMUDevice *hiod, void *opaque,
Error **errp)
{
VFIODevice *vdev = opaque;
hiod->name = g_strdup(vdev->name);
hiod->caps.aw_bits = vfio_device_get_aw_bits(vdev);
hiod->agent = opaque;
return true;
}
static int hiod_legacy_vfio_get_cap(HostIOMMUDevice *hiod, int cap,
Error **errp)
{
HostIOMMUDeviceCaps *caps = &hiod->caps;
switch (cap) {
case HOST_IOMMU_DEVICE_CAP_AW_BITS:
return caps->aw_bits;
default:
error_setg(errp, "%s: unsupported capability %x", hiod->name, cap);
return -EINVAL;
}
}
static GList *
hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp)
{
VFIODevice *vdev = hiod->agent;
GList *l = NULL;
g_assert(vdev);
if (vdev->bcontainer) {
l = g_list_copy(vdev->bcontainer->iova_ranges);
}
return l;
}
static void vfio_iommu_legacy_instance_init(Object *obj)
{
VFIOContainer *container = VFIO_IOMMU_LEGACY(obj);
QLIST_INIT(&container->group_list);
}
static void hiod_legacy_vfio_class_init(ObjectClass *oc, void *data)
{
HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc);
hioc->realize = hiod_legacy_vfio_realize;
hioc->get_cap = hiod_legacy_vfio_get_cap;
hioc->get_iova_ranges = hiod_legacy_vfio_get_iova_ranges;
};
static const TypeInfo types[] = {
{
.name = TYPE_VFIO_IOMMU_LEGACY,
.parent = TYPE_VFIO_IOMMU,
.instance_init = vfio_iommu_legacy_instance_init,
.instance_size = sizeof(VFIOContainer),
.class_init = vfio_iommu_legacy_class_init,
},
}, {
.name = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO,
.parent = TYPE_HOST_IOMMU_DEVICE,
.class_init = hiod_legacy_vfio_class_init,
}
};
DEFINE_TYPES(types)

View File

@ -658,3 +658,20 @@ void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops,
vbasedev->ram_block_discard_allowed = ram_discard;
}
int vfio_device_get_aw_bits(VFIODevice *vdev)
{
/*
* iova_ranges is a sorted list. For old kernels that support
* VFIO but not support query of iova ranges, iova_ranges is NULL,
* in this case HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX(64) is returned.
*/
GList *l = g_list_last(vdev->bcontainer->iova_ranges);
if (l) {
Range *range = l->data;
return range_get_last_bit(range) + 1;
}
return HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX;
}

View File

@ -237,9 +237,8 @@ static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container)
return;
}
memory_listener_unregister(&bcontainer->listener);
vfio_container_destroy(bcontainer);
iommufd_backend_free_id(container->be, container->ioas_id);
g_free(container);
object_unref(container);
}
static int iommufd_cdev_ram_block_discard_disable(bool state)
@ -324,7 +323,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
/* try to attach to an existing container in this space */
QLIST_FOREACH(bcontainer, &space->containers, next) {
container = container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer);
if (bcontainer->ops != iommufd_vioc ||
if (VFIO_IOMMU_GET_CLASS(bcontainer) != iommufd_vioc ||
vbasedev->iommufd != container->be) {
continue;
}
@ -352,13 +351,12 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
trace_iommufd_cdev_alloc_ioas(vbasedev->iommufd->fd, ioas_id);
container = g_malloc0(sizeof(*container));
container = VFIO_IOMMU_IOMMUFD(object_new(TYPE_VFIO_IOMMU_IOMMUFD));
container->be = vbasedev->iommufd;
container->ioas_id = ioas_id;
bcontainer = &container->bcontainer;
vfio_container_init(bcontainer, space, iommufd_vioc);
QLIST_INSERT_HEAD(&space->containers, bcontainer, next);
vfio_address_space_insert(space, bcontainer);
if (!iommufd_cdev_attach_container(vbasedev, container, errp)) {
goto err_attach_container;
@ -465,7 +463,7 @@ static VFIODevice *iommufd_cdev_pci_find_by_devid(__u32 devid)
VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD));
QLIST_FOREACH(vbasedev_iter, &vfio_device_list, global_next) {
if (vbasedev_iter->bcontainer->ops != iommufd_vioc) {
if (VFIO_IOMMU_GET_CLASS(vbasedev_iter->bcontainer) != iommufd_vioc) {
continue;
}
if (devid == vbasedev_iter->devid) {
@ -612,6 +610,8 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, void *data)
{
VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass);
vioc->hiod_typename = TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO;
vioc->dma_map = iommufd_cdev_map;
vioc->dma_unmap = iommufd_cdev_unmap;
vioc->attach_device = iommufd_cdev_attach;
@ -619,12 +619,64 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, void *data)
vioc->pci_hot_reset = iommufd_cdev_pci_hot_reset;
};
static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque,
Error **errp)
{
VFIODevice *vdev = opaque;
HostIOMMUDeviceCaps *caps = &hiod->caps;
enum iommu_hw_info_type type;
union {
struct iommu_hw_info_vtd vtd;
} data;
hiod->agent = opaque;
if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid,
&type, &data, sizeof(data), errp)) {
return false;
}
hiod->name = g_strdup(vdev->name);
caps->type = type;
caps->aw_bits = vfio_device_get_aw_bits(vdev);
return true;
}
static GList *
hiod_iommufd_vfio_get_iova_ranges(HostIOMMUDevice *hiod, Error **errp)
{
VFIODevice *vdev = hiod->agent;
GList *l = NULL;
g_assert(vdev);
if (vdev->bcontainer) {
l = g_list_copy(vdev->bcontainer->iova_ranges);
}
return l;
}
static void hiod_iommufd_vfio_class_init(ObjectClass *oc, void *data)
{
HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc);
hiodc->realize = hiod_iommufd_vfio_realize;
hiodc->get_iova_ranges = hiod_iommufd_vfio_get_iova_ranges;
};
static const TypeInfo types[] = {
{
.name = TYPE_VFIO_IOMMU_IOMMUFD,
.parent = TYPE_VFIO_IOMMU,
.instance_size = sizeof(VFIOIOMMUFDContainer),
.class_init = vfio_iommu_iommufd_class_init,
},
}, {
.name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO,
.parent = TYPE_HOST_IOMMU_DEVICE_IOMMUFD,
.class_init = hiod_iommufd_vfio_class_init,
}
};
DEFINE_TYPES(types)

View File

@ -2511,9 +2511,9 @@ int vfio_pci_get_pci_hot_reset_info(VFIOPCIDevice *vdev,
static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single)
{
VFIODevice *vbasedev = &vdev->vbasedev;
const VFIOIOMMUClass *ops = vbasedev->bcontainer->ops;
const VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer);
return ops->pci_hot_reset(vbasedev, single);
return vioc->pci_hot_reset(vbasedev, single);
}
/*
@ -3121,10 +3121,15 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
vfio_bars_register(vdev);
if (!vfio_add_capabilities(vdev, errp)) {
if (!pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) {
error_prepend(errp, "Failed to set iommu_device: ");
goto out_teardown;
}
if (!vfio_add_capabilities(vdev, errp)) {
goto out_unset_idev;
}
if (vdev->vga) {
vfio_vga_quirk_setup(vdev);
}
@ -3141,7 +3146,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
error_setg(errp,
"cannot support IGD OpRegion feature on hotplugged "
"device");
goto out_teardown;
goto out_unset_idev;
}
ret = vfio_get_dev_region_info(vbasedev,
@ -3150,11 +3155,11 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
if (ret) {
error_setg_errno(errp, -ret,
"does not support requested IGD OpRegion feature");
goto out_teardown;
goto out_unset_idev;
}
if (!vfio_pci_igd_opregion_init(vdev, opregion, errp)) {
goto out_teardown;
goto out_unset_idev;
}
}
@ -3238,6 +3243,8 @@ out_deregister:
if (vdev->intx.mmap_timer) {
timer_free(vdev->intx.mmap_timer);
}
out_unset_idev:
pci_device_unset_iommu_device(pdev);
out_teardown:
vfio_teardown_msi(vdev);
vfio_bars_exit(vdev);
@ -3266,6 +3273,7 @@ static void vfio_instance_finalize(Object *obj)
static void vfio_exitfn(PCIDevice *pdev)
{
VFIOPCIDevice *vdev = VFIO_PCI(pdev);
VFIODevice *vbasedev = &vdev->vbasedev;
vfio_unregister_req_notifier(vdev);
vfio_unregister_err_notifier(vdev);
@ -3280,7 +3288,8 @@ static void vfio_exitfn(PCIDevice *pdev)
vfio_teardown_msi(vdev);
vfio_pci_disable_rp_atomics(vdev);
vfio_bars_exit(vdev);
vfio_migration_exit(&vdev->vbasedev);
vfio_migration_exit(vbasedev);
pci_device_unset_iommu_device(pdev);
}
static void vfio_pci_reset(DeviceState *dev)

View File

@ -30,6 +30,8 @@ typedef struct VFIOSpaprContainer {
QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list;
} VFIOSpaprContainer;
OBJECT_DECLARE_SIMPLE_TYPE(VFIOSpaprContainer, VFIO_IOMMU_SPAPR);
static bool vfio_prereg_listener_skipped_section(MemoryRegionSection *section)
{
if (memory_region_is_iommu(section->mr)) {
@ -548,6 +550,7 @@ static const TypeInfo types[] = {
{
.name = TYPE_VFIO_IOMMU_SPAPR,
.parent = TYPE_VFIO_IOMMU_LEGACY,
.instance_size = sizeof(VFIOSpaprContainer),
.class_init = vfio_iommu_spapr_class_init,
},
};

View File

@ -69,6 +69,11 @@ typedef struct VirtIOIOMMUMapping {
uint32_t flags;
} VirtIOIOMMUMapping;
struct hiod_key {
PCIBus *bus;
uint8_t devfn;
};
static inline uint16_t virtio_iommu_get_bdf(IOMMUDevice *dev)
{
return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn);
@ -462,8 +467,195 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
return &sdev->as;
}
static gboolean hiod_equal(gconstpointer v1, gconstpointer v2)
{
const struct hiod_key *key1 = v1;
const struct hiod_key *key2 = v2;
return (key1->bus == key2->bus) && (key1->devfn == key2->devfn);
}
static guint hiod_hash(gconstpointer v)
{
const struct hiod_key *key = v;
guint value = (guint)(uintptr_t)key->bus;
return (guint)(value << 8 | key->devfn);
}
static void hiod_destroy(gpointer v)
{
object_unref(v);
}
static HostIOMMUDevice *
get_host_iommu_device(VirtIOIOMMU *viommu, PCIBus *bus, int devfn) {
struct hiod_key key = {
.bus = bus,
.devfn = devfn,
};
return g_hash_table_lookup(viommu->host_iommu_devices, &key);
}
/**
* rebuild_resv_regions: rebuild resv regions with both the
* info of host resv ranges and property set resv ranges
*/
static int rebuild_resv_regions(IOMMUDevice *sdev)
{
GList *l;
int i = 0;
/* free the existing list and rebuild it from scratch */
g_list_free_full(sdev->resv_regions, g_free);
sdev->resv_regions = NULL;
/* First add host reserved regions if any, all tagged as RESERVED */
for (l = sdev->host_resv_ranges; l; l = l->next) {
ReservedRegion *reg = g_new0(ReservedRegion, 1);
Range *r = (Range *)l->data;
reg->type = VIRTIO_IOMMU_RESV_MEM_T_RESERVED;
range_set_bounds(&reg->range, range_lob(r), range_upb(r));
sdev->resv_regions = resv_region_list_insert(sdev->resv_regions, reg);
trace_virtio_iommu_host_resv_regions(sdev->iommu_mr.parent_obj.name, i,
range_lob(&reg->range),
range_upb(&reg->range));
i++;
}
/*
* then add higher priority reserved regions set by the machine
* through properties
*/
add_prop_resv_regions(sdev);
return 0;
}
static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus,
int devfn, GList *iova_ranges,
Error **errp)
{
IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
IOMMUDevice *sdev;
GList *current_ranges;
GList *l, *tmp, *new_ranges = NULL;
int ret = -EINVAL;
if (!sbus) {
error_report("%s no sbus", __func__);
}
sdev = sbus->pbdev[devfn];
current_ranges = sdev->host_resv_ranges;
g_assert(!sdev->probe_done);
/* check that each new resv region is included in an existing one */
if (sdev->host_resv_ranges) {
range_inverse_array(iova_ranges,
&new_ranges,
0, UINT64_MAX);
for (tmp = new_ranges; tmp; tmp = tmp->next) {
Range *newr = (Range *)tmp->data;
bool included = false;
for (l = current_ranges; l; l = l->next) {
Range * r = (Range *)l->data;
if (range_contains_range(r, newr)) {
included = true;
break;
}
}
if (!included) {
goto error;
}
}
/* all new reserved ranges are included in existing ones */
ret = 0;
goto out;
}
range_inverse_array(iova_ranges,
&sdev->host_resv_ranges,
0, UINT64_MAX);
rebuild_resv_regions(sdev);
return 0;
error:
error_setg(errp, "%s Conflicting host reserved ranges set!",
__func__);
out:
g_list_free_full(new_ranges, g_free);
return ret;
}
static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
HostIOMMUDevice *hiod, Error **errp)
{
VirtIOIOMMU *viommu = opaque;
HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod);
struct hiod_key *new_key;
GList *host_iova_ranges = NULL;
assert(hiod);
if (get_host_iommu_device(viommu, bus, devfn)) {
error_setg(errp, "Host IOMMU device already exists");
return false;
}
if (hiodc->get_iova_ranges) {
int ret;
host_iova_ranges = hiodc->get_iova_ranges(hiod, errp);
if (!host_iova_ranges) {
return true; /* some old kernels may not support that capability */
}
ret = virtio_iommu_set_host_iova_ranges(viommu, hiod->aliased_bus,
hiod->aliased_devfn,
host_iova_ranges, errp);
if (ret) {
g_list_free_full(host_iova_ranges, g_free);
return false;
}
}
new_key = g_malloc(sizeof(*new_key));
new_key->bus = bus;
new_key->devfn = devfn;
object_ref(hiod);
g_hash_table_insert(viommu->host_iommu_devices, new_key, hiod);
g_list_free_full(host_iova_ranges, g_free);
return true;
}
static void
virtio_iommu_unset_iommu_device(PCIBus *bus, void *opaque, int devfn)
{
VirtIOIOMMU *viommu = opaque;
HostIOMMUDevice *hiod;
struct hiod_key key = {
.bus = bus,
.devfn = devfn,
};
hiod = g_hash_table_lookup(viommu->host_iommu_devices, &key);
if (!hiod) {
return;
}
g_hash_table_remove(viommu->host_iommu_devices, &key);
}
static const PCIIOMMUOps virtio_iommu_ops = {
.get_address_space = virtio_iommu_find_add_as,
.set_iommu_device = virtio_iommu_set_iommu_device,
.unset_iommu_device = virtio_iommu_unset_iommu_device,
};
static int virtio_iommu_attach(VirtIOIOMMU *s,
@ -1159,106 +1351,6 @@ static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr,
return 0;
}
/**
* rebuild_resv_regions: rebuild resv regions with both the
* info of host resv ranges and property set resv ranges
*/
static int rebuild_resv_regions(IOMMUDevice *sdev)
{
GList *l;
int i = 0;
/* free the existing list and rebuild it from scratch */
g_list_free_full(sdev->resv_regions, g_free);
sdev->resv_regions = NULL;
/* First add host reserved regions if any, all tagged as RESERVED */
for (l = sdev->host_resv_ranges; l; l = l->next) {
ReservedRegion *reg = g_new0(ReservedRegion, 1);
Range *r = (Range *)l->data;
reg->type = VIRTIO_IOMMU_RESV_MEM_T_RESERVED;
range_set_bounds(&reg->range, range_lob(r), range_upb(r));
sdev->resv_regions = resv_region_list_insert(sdev->resv_regions, reg);
trace_virtio_iommu_host_resv_regions(sdev->iommu_mr.parent_obj.name, i,
range_lob(&reg->range),
range_upb(&reg->range));
i++;
}
/*
* then add higher priority reserved regions set by the machine
* through properties
*/
add_prop_resv_regions(sdev);
return 0;
}
/**
* virtio_iommu_set_iova_ranges: Conveys the usable IOVA ranges
*
* The function turns those into reserved ranges. Once some
* reserved ranges have been set, new reserved regions cannot be
* added outside of the original ones.
*
* @mr: IOMMU MR
* @iova_ranges: list of usable IOVA ranges
* @errp: error handle
*/
static int virtio_iommu_set_iova_ranges(IOMMUMemoryRegion *mr,
GList *iova_ranges,
Error **errp)
{
IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr);
GList *current_ranges = sdev->host_resv_ranges;
GList *l, *tmp, *new_ranges = NULL;
int ret = -EINVAL;
/* check that each new resv region is included in an existing one */
if (sdev->host_resv_ranges) {
range_inverse_array(iova_ranges,
&new_ranges,
0, UINT64_MAX);
for (tmp = new_ranges; tmp; tmp = tmp->next) {
Range *newr = (Range *)tmp->data;
bool included = false;
for (l = current_ranges; l; l = l->next) {
Range * r = (Range *)l->data;
if (range_contains_range(r, newr)) {
included = true;
break;
}
}
if (!included) {
goto error;
}
}
/* all new reserved ranges are included in existing ones */
ret = 0;
goto out;
}
if (sdev->probe_done) {
warn_report("%s: Notified about new host reserved regions after probe",
mr->parent_obj.name);
}
range_inverse_array(iova_ranges,
&sdev->host_resv_ranges,
0, UINT64_MAX);
rebuild_resv_regions(sdev);
return 0;
error:
error_setg(errp, "IOMMU mr=%s Conflicting host reserved ranges set!",
mr->parent_obj.name);
out:
g_list_free_full(new_ranges, g_free);
return ret;
}
static void virtio_iommu_system_reset(void *opaque)
{
VirtIOIOMMU *s = opaque;
@ -1357,6 +1449,9 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
s->as_by_busptr = g_hash_table_new_full(NULL, NULL, NULL, g_free);
s->host_iommu_devices = g_hash_table_new_full(hiod_hash, hiod_equal,
g_free, hiod_destroy);
if (s->primary_bus) {
pci_setup_iommu(s->primary_bus, &virtio_iommu_ops, s);
} else {
@ -1581,7 +1676,6 @@ static void virtio_iommu_memory_region_class_init(ObjectClass *klass,
imrc->replay = virtio_iommu_replay;
imrc->notify_flag_changed = virtio_iommu_notify_flag_changed;
imrc->iommu_set_page_size_mask = virtio_iommu_set_page_size_mask;
imrc->iommu_set_iova_ranges = virtio_iommu_set_iova_ranges;
}
static const TypeInfo virtio_iommu_info = {

View File

@ -530,26 +530,6 @@ struct IOMMUMemoryRegionClass {
int (*iommu_set_page_size_mask)(IOMMUMemoryRegion *iommu,
uint64_t page_size_mask,
Error **errp);
/**
* @iommu_set_iova_ranges:
*
* Propagate information about the usable IOVA ranges for a given IOMMU
* memory region. Used for example to propagate host physical device
* reserved memory region constraints to the virtual IOMMU.
*
* Optional method: if this method is not provided, then the default IOVA
* aperture is used.
*
* @iommu: the IOMMUMemoryRegion
*
* @iova_ranges: list of ordered IOVA ranges (at least one range)
*
* Returns 0 on success, or a negative error. In case of failure, the error
* object must be created.
*/
int (*iommu_set_iova_ranges)(IOMMUMemoryRegion *iommu,
GList *iova_ranges,
Error **errp);
};
typedef struct RamDiscardListener RamDiscardListener;
@ -1951,18 +1931,6 @@ int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr,
uint64_t page_size_mask,
Error **errp);
/**
* memory_region_iommu_set_iova_ranges - Set the usable IOVA ranges
* for a given IOMMU MR region
*
* @iommu: IOMMU memory region
* @iova_ranges: list of ordered IOVA ranges (at least one range)
* @errp: pointer to Error*, to store an error if it happens.
*/
int memory_region_iommu_set_iova_ranges(IOMMUMemoryRegion *iommu,
GList *iova_ranges,
Error **errp);
/**
* memory_region_name: get a memory region's name
*

View File

@ -292,6 +292,8 @@ struct IntelIOMMUState {
/* list of registered notifiers */
QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers;
GHashTable *vtd_host_iommu_dev; /* HostIOMMUDevice */
/* interrupt remapping */
bool intr_enabled; /* Whether guest enabled IR */
dma_addr_t intr_root; /* Interrupt remapping table pointer */

View File

@ -3,6 +3,7 @@
#include "exec/memory.h"
#include "sysemu/dma.h"
#include "sysemu/host_iommu_device.h"
/* PCI includes legacy ISA access. */
#include "hw/isa/isa.h"
@ -383,10 +384,45 @@ typedef struct PCIIOMMUOps {
*
* @devfn: device and function number
*/
AddressSpace * (*get_address_space)(PCIBus *bus, void *opaque, int devfn);
AddressSpace * (*get_address_space)(PCIBus *bus, void *opaque, int devfn);
/**
* @set_iommu_device: attach a HostIOMMUDevice to a vIOMMU
*
* Optional callback, if not implemented in vIOMMU, then vIOMMU can't
* retrieve host information from the associated HostIOMMUDevice.
*
* @bus: the #PCIBus of the PCI device.
*
* @opaque: the data passed to pci_setup_iommu().
*
* @devfn: device and function number of the PCI device.
*
* @dev: the #HostIOMMUDevice to attach.
*
* @errp: pass an Error out only when return false
*
* Returns: true if HostIOMMUDevice is attached or else false with errp set.
*/
bool (*set_iommu_device)(PCIBus *bus, void *opaque, int devfn,
HostIOMMUDevice *dev, Error **errp);
/**
* @unset_iommu_device: detach a HostIOMMUDevice from a vIOMMU
*
* Optional callback.
*
* @bus: the #PCIBus of the PCI device.
*
* @opaque: the data passed to pci_setup_iommu().
*
* @devfn: device and function number of the PCI device.
*/
void (*unset_iommu_device)(PCIBus *bus, void *opaque, int devfn);
} PCIIOMMUOps;
AddressSpace *pci_device_iommu_address_space(PCIDevice *dev);
bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod,
Error **errp);
void pci_device_unset_iommu_device(PCIDevice *dev);
/**
* pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus

View File

@ -31,6 +31,8 @@
#endif
#include "sysemu/sysemu.h"
#include "hw/vfio/vfio-container-base.h"
#include "sysemu/host_iommu_device.h"
#include "sysemu/iommufd.h"
#define VFIO_MSG_PREFIX "vfio %s: "
@ -82,6 +84,8 @@ typedef struct VFIOContainer {
QLIST_HEAD(, VFIOGroup) group_list;
} VFIOContainer;
OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY);
typedef struct VFIOHostDMAWindow {
hwaddr min_iova;
hwaddr max_iova;
@ -97,6 +101,8 @@ typedef struct VFIOIOMMUFDContainer {
uint32_t ioas_id;
} VFIOIOMMUFDContainer;
OBJECT_DECLARE_SIMPLE_TYPE(VFIOIOMMUFDContainer, VFIO_IOMMU_IOMMUFD);
typedef struct VFIODeviceOps VFIODeviceOps;
typedef struct VFIODevice {
@ -125,6 +131,7 @@ typedef struct VFIODevice {
OnOffAuto pre_copy_dirty_page_tracking;
bool dirty_pages_supported;
bool dirty_tracking;
HostIOMMUDevice *hiod;
int devid;
IOMMUFDBackend *iommufd;
} VFIODevice;
@ -171,6 +178,10 @@ typedef struct VFIOGroup {
bool ram_block_discard_allowed;
} VFIOGroup;
#define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio"
#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \
TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio"
typedef struct VFIODMABuf {
QemuDmaBuf *buf;
uint32_t pos_x, pos_y, pos_updates;
@ -199,10 +210,8 @@ typedef struct VFIODisplay {
VFIOAddressSpace *vfio_get_address_space(AddressSpace *as);
void vfio_put_address_space(VFIOAddressSpace *space);
/* SPAPR specific */
int vfio_spapr_container_init(VFIOContainer *container, Error **errp);
void vfio_spapr_container_deinit(VFIOContainer *container);
void vfio_address_space_insert(VFIOAddressSpace *space,
VFIOContainerBase *bcontainer);
void vfio_disable_irqindex(VFIODevice *vbasedev, int index);
void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index);
@ -283,4 +292,5 @@ bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp);
void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp);
void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops,
DeviceState *dev, bool ram_discard);
int vfio_device_get_aw_bits(VFIODevice *vdev);
#endif /* HW_VFIO_VFIO_COMMON_H */

View File

@ -34,7 +34,7 @@ typedef struct VFIOAddressSpace {
* This is the base object for vfio container backends
*/
typedef struct VFIOContainerBase {
const VFIOIOMMUClass *ops;
Object parent;
VFIOAddressSpace *space;
MemoryListener listener;
Error *error;
@ -86,28 +86,18 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer,
int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp);
void vfio_container_init(VFIOContainerBase *bcontainer,
VFIOAddressSpace *space,
const VFIOIOMMUClass *ops);
void vfio_container_destroy(VFIOContainerBase *bcontainer);
#define TYPE_VFIO_IOMMU "vfio-iommu"
#define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy"
#define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr"
#define TYPE_VFIO_IOMMU_IOMMUFD TYPE_VFIO_IOMMU "-iommufd"
/*
* VFIOContainerBase is not an abstract QOM object because it felt
* unnecessary to expose all the IOMMU backends to the QEMU machine
* and human interface. However, we can still abstract the IOMMU
* backend handlers using a QOM interface class. This provides more
* flexibility when referencing the various implementations.
*/
DECLARE_CLASS_CHECKERS(VFIOIOMMUClass, VFIO_IOMMU, TYPE_VFIO_IOMMU)
OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU)
struct VFIOIOMMUClass {
InterfaceClass parent_class;
ObjectClass parent_class;
/* Properties */
const char *hiod_typename;
/* basic feature */
bool (*setup)(VFIOContainerBase *bcontainer, Error **errp);

View File

@ -25,6 +25,7 @@
#include "hw/pci/pci.h"
#include "qom/object.h"
#include "qapi/qapi-types-virtio.h"
#include "sysemu/host_iommu_device.h"
#define TYPE_VIRTIO_IOMMU "virtio-iommu-device"
#define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-pci"
@ -57,6 +58,7 @@ struct VirtIOIOMMU {
struct virtio_iommu_config config;
uint64_t features;
GHashTable *as_by_busptr;
GHashTable *host_iommu_devices;
IOMMUPciBus *iommu_pcibus_by_bus_num[PCI_BUS_MAX];
PCIBus *primary_bus;
ReservedRegion *prop_resv_regions;

View File

@ -20,6 +20,8 @@
#ifndef QEMU_RANGE_H
#define QEMU_RANGE_H
#include "qemu/bitops.h"
/*
* Operations on 64 bit address ranges.
* Notes:
@ -217,6 +219,15 @@ static inline int ranges_overlap(uint64_t first1, uint64_t len1,
return !(last2 < first1 || last1 < first2);
}
/* Get highest non-zero bit position of a range */
static inline int range_get_last_bit(Range *range)
{
if (range_is_empty(range)) {
return -1;
}
return 63 - clz64(range->upb);
}
/*
* Return -1 if @a < @b, 1 @a > @b, and 0 if they touch or overlap.
* Both @a and @b must not be empty.

View File

@ -0,0 +1,102 @@
/*
* Host IOMMU device abstract declaration
*
* Copyright (C) 2024 Intel Corporation.
*
* Authors: Zhenzhong Duan <zhenzhong.duan@intel.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*/
#ifndef HOST_IOMMU_DEVICE_H
#define HOST_IOMMU_DEVICE_H
#include "qom/object.h"
#include "qapi/error.h"
/**
* struct HostIOMMUDeviceCaps - Define host IOMMU device capabilities.
*
* @type: host platform IOMMU type.
*
* @aw_bits: host IOMMU address width. 0xff if no limitation.
*/
typedef struct HostIOMMUDeviceCaps {
uint32_t type;
uint8_t aw_bits;
} HostIOMMUDeviceCaps;
#define TYPE_HOST_IOMMU_DEVICE "host-iommu-device"
OBJECT_DECLARE_TYPE(HostIOMMUDevice, HostIOMMUDeviceClass, HOST_IOMMU_DEVICE)
struct HostIOMMUDevice {
Object parent_obj;
char *name;
void *agent; /* pointer to agent device, ie. VFIO or VDPA device */
PCIBus *aliased_bus;
int aliased_devfn;
HostIOMMUDeviceCaps caps;
};
/**
* struct HostIOMMUDeviceClass - The base class for all host IOMMU devices.
*
* Different types of host devices (e.g., VFIO or VDPA device) or devices
* with different backend (e.g., VFIO legacy container or IOMMUFD backend)
* will have different implementations of the HostIOMMUDeviceClass.
*/
struct HostIOMMUDeviceClass {
ObjectClass parent_class;
/**
* @realize: initialize host IOMMU device instance further.
*
* Mandatory callback.
*
* @hiod: pointer to a host IOMMU device instance.
*
* @opaque: pointer to agent device of this host IOMMU device,
* e.g., VFIO base device or VDPA device.
*
* @errp: pass an Error out when realize fails.
*
* Returns: true on success, false on failure.
*/
bool (*realize)(HostIOMMUDevice *hiod, void *opaque, Error **errp);
/**
* @get_cap: check if a host IOMMU device capability is supported.
*
* Optional callback, if not implemented, hint not supporting query
* of @cap.
*
* @hiod: pointer to a host IOMMU device instance.
*
* @cap: capability to check.
*
* @errp: pass an Error out when fails to query capability.
*
* Returns: <0 on failure, 0 if a @cap is unsupported, or else
* 1 or some positive value for some special @cap,
* i.e., HOST_IOMMU_DEVICE_CAP_AW_BITS.
*/
int (*get_cap)(HostIOMMUDevice *hiod, int cap, Error **errp);
/**
* @get_iova_ranges: Return the list of usable iova_ranges along with
* @hiod Host IOMMU device
*
* @hiod: handle to the host IOMMU device
* @errp: error handle
*/
GList* (*get_iova_ranges)(HostIOMMUDevice *hiod, Error **errp);
};
/*
* Host IOMMU device capability list.
*/
#define HOST_IOMMU_DEVICE_CAP_IOMMU_TYPE 0
#define HOST_IOMMU_DEVICE_CAP_AW_BITS 1
#define HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX 64
#endif

View File

@ -1,9 +1,23 @@
/*
* iommufd container backend declaration
*
* Copyright (C) 2024 Intel Corporation.
* Copyright Red Hat, Inc. 2024
*
* Authors: Yi Liu <yi.l.liu@intel.com>
* Eric Auger <eric.auger@redhat.com>
* Zhenzhong Duan <zhenzhong.duan@intel.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef SYSEMU_IOMMUFD_H
#define SYSEMU_IOMMUFD_H
#include "qom/object.h"
#include "exec/hwaddr.h"
#include "exec/cpu-common.h"
#include "sysemu/host_iommu_device.h"
#define TYPE_IOMMUFD_BACKEND "iommufd"
OBJECT_DECLARE_TYPE(IOMMUFDBackend, IOMMUFDBackendClass, IOMMUFD_BACKEND)
@ -33,4 +47,9 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
ram_addr_t size, void *vaddr, bool readonly);
int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
hwaddr iova, ram_addr_t size);
bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid,
uint32_t *type, void *data, uint32_t len,
Error **errp);
#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd"
#endif

View File

@ -1914,19 +1914,6 @@ int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr,
return ret;
}
int memory_region_iommu_set_iova_ranges(IOMMUMemoryRegion *iommu_mr,
GList *iova_ranges,
Error **errp)
{
IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
int ret = 0;
if (imrc->iommu_set_iova_ranges) {
ret = imrc->iommu_set_iova_ranges(iommu_mr, iova_ranges, errp);
}
return ret;
}
int memory_region_register_iommu_notifier(MemoryRegion *mr,
IOMMUNotifier *n, Error **errp)
{