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:
commit
3f044554b9
@ -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
|
||||
|
33
backends/host_iommu_device.c
Normal file
33
backends/host_iommu_device.c
Normal 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);
|
||||
}
|
@ -208,7 +208,52 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const TypeInfo iommufd_backend_info = {
|
||||
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),
|
||||
@ -220,11 +265,12 @@ static const TypeInfo iommufd_backend_info = {
|
||||
{ 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)
|
||||
|
@ -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'))
|
||||
|
@ -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. */
|
||||
|
79
hw/pci/pci.c
79
hw/pci/pci.c
@ -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)
|
||||
{
|
||||
/*
|
||||
|
130
hw/vfio/common.c
130
hw/vfio/common.c
@ -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,9 +879,25 @@ static void vfio_dirty_tracking_update(MemoryListener *listener,
|
||||
}
|
||||
|
||||
trace_vfio_device_dirty_tracking_update(iova, end, *min, *max);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.name = "vfio-tracking",
|
||||
.region_add = vfio_dirty_tracking_update,
|
||||
@ -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_dirty_bitmap(VFIOContainerBase *bcontainer,
|
||||
MemoryRegionSection *section, Error **errp)
|
||||
static int vfio_sync_iommu_dirty_bitmap(VFIOContainerBase *bcontainer,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
ram_addr_t ram_addr;
|
||||
|
||||
if (memory_region_is_iommu(section->mr)) {
|
||||
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) {
|
||||
Int128 llend;
|
||||
vfio_giommu_dirty_notifier gdn = { .giommu = giommu };
|
||||
int idx = memory_region_iommu_attrs_to_index(giommu->iommu_mr,
|
||||
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),
|
||||
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;
|
||||
}
|
||||
|
||||
static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer,
|
||||
MemoryRegionSection *section, Error **errp)
|
||||
{
|
||||
ram_addr_t ram_addr;
|
||||
|
||||
if (memory_region_is_iommu(section->mr)) {
|
||||
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);
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
vioc = vfio_get_iommu_class(iommu_type, errp);
|
||||
if (!vioc) {
|
||||
error_setg(errp, "No available IOMMU models");
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
vfio_container_init(&container->bcontainer, space, vioc);
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
@ -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(®->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(®->range),
|
||||
range_upb(®->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(®->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(®->range),
|
||||
range_upb(®->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 = {
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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 */
|
||||
|
@ -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"
|
||||
@ -384,9 +385,44 @@ typedef struct PCIIOMMUOps {
|
||||
* @devfn: device and function number
|
||||
*/
|
||||
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
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
102
include/sysemu/host_iommu_device.h
Normal file
102
include/sysemu/host_iommu_device.h
Normal 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
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user