vfio queue:
* Coverity fix * IGD cleanups using VFIOQuirk * SIGSEV fix in IOMMUFD host IOMMU device * Improved error reporting for MMIO region mapping failures -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmerTzQACgkQUaNDx8/7 7KHXLw/+LaONyFor+kuorb5et6rzyrE4keIUDv8zDTM4FnwFKP31wX8feQ63o17U DQUYN4uM5Ah/PemF/IBCj44x1Eirzl8LW51sMtxg/weCa8xrZOsHjmoKNml4f+zs ERzO/KSu9PWEWEyX79XGCcu5VQKl60b8Ra5QMBNKZKjVZpfBTxCjHZFIvQxSJFvm gPKHFDtmtbhtBnq3U/N/PwpnUuH4+p6ofz1eKdOcin11CAks7cAt6bl1CIs7sUbC ttrrQg6D+UJ5b+ISZjsw5hakfRIdtlZ/lS4jk678gN06108CIMmFPLPaRu27mdX9 4wBiMSQM8fFbbHw66FQiPgJeeGAmG/PvdLN4SbRSujkEkKyEyqtH2dRINy9PNXj4 ZXXugx7xqfPfTEC1lwsyGDdHdHH022V0ScdDpx+K87klRvu30ZjorB7QSCI7x+ZN yW2ztZQ2hNH6MsgrKTQS6MLArHgU+h0ycaHy+01jj5UKSs3xAf53wNnx2uoBmKGj gZB/tNFg60qeSuW900ybnBTaH60qLs6xzY7evDRa5cqPYJ+z/lRUYp45fmsgQ1yR 91PHhC/mQLFjQRc78vF6k7slMm/Fk8JOalZgYPtm+Atdw3ufjOexavoHqN3Sa1Us keKnR589kHikPd3zZN7sZzT8wMNTAcRbSy72360+PzEZ1Iiiu+M= =wpSw -----END PGP SIGNATURE----- Merge tag 'pull-vfio-20250211' of https://github.com/legoater/qemu into staging vfio queue: * Coverity fix * IGD cleanups using VFIOQuirk * SIGSEV fix in IOMMUFD host IOMMU device * Improved error reporting for MMIO region mapping failures # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmerTzQACgkQUaNDx8/7 # 7KHXLw/+LaONyFor+kuorb5et6rzyrE4keIUDv8zDTM4FnwFKP31wX8feQ63o17U # DQUYN4uM5Ah/PemF/IBCj44x1Eirzl8LW51sMtxg/weCa8xrZOsHjmoKNml4f+zs # ERzO/KSu9PWEWEyX79XGCcu5VQKl60b8Ra5QMBNKZKjVZpfBTxCjHZFIvQxSJFvm # gPKHFDtmtbhtBnq3U/N/PwpnUuH4+p6ofz1eKdOcin11CAks7cAt6bl1CIs7sUbC # ttrrQg6D+UJ5b+ISZjsw5hakfRIdtlZ/lS4jk678gN06108CIMmFPLPaRu27mdX9 # 4wBiMSQM8fFbbHw66FQiPgJeeGAmG/PvdLN4SbRSujkEkKyEyqtH2dRINy9PNXj4 # ZXXugx7xqfPfTEC1lwsyGDdHdHH022V0ScdDpx+K87klRvu30ZjorB7QSCI7x+ZN # yW2ztZQ2hNH6MsgrKTQS6MLArHgU+h0ycaHy+01jj5UKSs3xAf53wNnx2uoBmKGj # gZB/tNFg60qeSuW900ybnBTaH60qLs6xzY7evDRa5cqPYJ+z/lRUYp45fmsgQ1yR # 91PHhC/mQLFjQRc78vF6k7slMm/Fk8JOalZgYPtm+Atdw3ufjOexavoHqN3Sa1Us # keKnR589kHikPd3zZN7sZzT8wMNTAcRbSy72360+PzEZ1Iiiu+M= # =wpSw # -----END PGP SIGNATURE----- # gpg: Signature made Tue 11 Feb 2025 08:23:00 EST # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@redhat.com>" [full] # gpg: aka "Cédric Le Goater <clg@kaod.org>" [full] # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-vfio-20250211' of https://github.com/legoater/qemu: vfio: Remove superfluous error report in vfio_listener_region_add() vfio: Remove reports of DMA mapping errors in backends vfio: Improve error reporting when MMIO region mapping fails vfio: Introduce vfio_get_vfio_device() vfio: Rephrase comment in vfio_listener_region_add() error path vfio/pci: Replace "iommu_device" by "vIOMMU" util/error: Introduce warn_report_err_once() vfio/iommufd: Fix SIGSEV in iommufd_cdev_attach() vfio/igd: use VFIOConfigMirrorQuirk for mirrored registers vfio/pci: introduce config_offset field in VFIOConfigMirrorQuirk vfio/pci: declare generic quirks in a new header file vfio/igd: Fix potential overflow in igd_gtt_memory_size() Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
5f1de4d3ce
@ -167,8 +167,6 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
|
||||
/* TODO: Not support mapping hardware PCI BAR region for now. */
|
||||
if (errno == EFAULT) {
|
||||
warn_report("IOMMU_IOAS_MAP failed: %m, PCI BAR?");
|
||||
} else {
|
||||
error_report("IOMMU_IOAS_MAP failed: %m");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@ -203,7 +201,6 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
||||
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
error_report("IOMMU_IOAS_UNMAP failed: %m");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -555,6 +555,18 @@ static bool vfio_get_section_iova_range(VFIOContainerBase *bcontainer,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp)
|
||||
{
|
||||
/*
|
||||
* MMIO region mapping failures are not fatal but in this case PCI
|
||||
* peer-to-peer transactions are broken.
|
||||
*/
|
||||
if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) {
|
||||
error_append_hint(errp, "%s: PCI peer-to-peer transactions "
|
||||
"on BARs are not supported.\n", vbasedev->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void vfio_listener_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
@ -582,8 +594,9 @@ static void vfio_listener_region_add(MemoryListener *listener,
|
||||
return;
|
||||
}
|
||||
|
||||
/* PPC64/pseries machine only */
|
||||
if (!vfio_container_add_section_window(bcontainer, section, &err)) {
|
||||
goto fail;
|
||||
goto mmio_dma_error;
|
||||
}
|
||||
|
||||
memory_region_ref(section->mr);
|
||||
@ -668,9 +681,13 @@ static void vfio_listener_region_add(MemoryListener *listener,
|
||||
"0x%"HWADDR_PRIx", %p) = %d (%s)",
|
||||
bcontainer, iova, int128_get64(llsize), vaddr, ret,
|
||||
strerror(-ret));
|
||||
mmio_dma_error:
|
||||
if (memory_region_is_ram_device(section->mr)) {
|
||||
/* Allow unexpected mappings not to be fatal for RAM devices */
|
||||
error_report_err(err);
|
||||
VFIODevice *vbasedev =
|
||||
vfio_get_vfio_device(memory_region_owner(section->mr));
|
||||
vfio_device_error_append(vbasedev, &err);
|
||||
warn_report_err_once(err);
|
||||
return;
|
||||
}
|
||||
goto fail;
|
||||
@ -679,16 +696,12 @@ static void vfio_listener_region_add(MemoryListener *listener,
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (memory_region_is_ram_device(section->mr)) {
|
||||
error_reportf_err(err, "PCI p2p may not work: ");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* On the initfn path, store the first error in the container so we
|
||||
* can gracefully fail. Runtime, there's not much we can do other
|
||||
* than throw a hardware error.
|
||||
*/
|
||||
if (!bcontainer->initialized) {
|
||||
/*
|
||||
* At machine init time or when the device is attached to the
|
||||
* VM, store the first error in the container so we can
|
||||
* gracefully fail the device realize routine.
|
||||
*/
|
||||
if (!bcontainer->error) {
|
||||
error_propagate_prepend(&bcontainer->error, err,
|
||||
"Region %s: ",
|
||||
@ -697,6 +710,10 @@ fail:
|
||||
error_free(err);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* At runtime, there's not much we can do other than throw a
|
||||
* hardware error.
|
||||
*/
|
||||
error_report_err(err);
|
||||
hw_error("vfio: DMA mapping failed, unable to continue");
|
||||
}
|
||||
@ -786,6 +803,7 @@ static void vfio_listener_region_del(MemoryListener *listener,
|
||||
|
||||
memory_region_unref(section->mr);
|
||||
|
||||
/* PPC64/pseries machine only */
|
||||
vfio_container_del_section_window(bcontainer, section);
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,6 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer,
|
||||
unmap.size -= 1ULL << ctz64(bcontainer->pgsizes);
|
||||
continue;
|
||||
}
|
||||
error_report("VFIO_UNMAP_DMA failed: %s", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
@ -204,7 +203,6 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova,
|
||||
return 0;
|
||||
}
|
||||
|
||||
error_report("VFIO_MAP_DMA failed: %s", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "hw/vfio/vfio-common.h"
|
||||
#include "hw/vfio/pci.h"
|
||||
#include "hw/hw.h"
|
||||
#include "trace.h"
|
||||
#include "qapi/error.h"
|
||||
@ -728,3 +729,12 @@ bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp)
|
||||
|
||||
return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp);
|
||||
}
|
||||
|
||||
VFIODevice *vfio_get_vfio_device(Object *obj)
|
||||
{
|
||||
if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) {
|
||||
return &VFIO_PCI(obj)->vbasedev;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
127
hw/vfio/igd.c
127
hw/vfio/igd.c
@ -18,6 +18,7 @@
|
||||
#include "hw/hw.h"
|
||||
#include "hw/nvram/fw_cfg.h"
|
||||
#include "pci.h"
|
||||
#include "pci-quirks.h"
|
||||
#include "trace.h"
|
||||
|
||||
/*
|
||||
@ -133,7 +134,7 @@ static uint64_t igd_gtt_memory_size(int gen, uint16_t gmch)
|
||||
} else {
|
||||
ggms = (gmch >> IGD_GMCH_GEN8_GGMS_SHIFT) & IGD_GMCH_GEN8_GGMS_MASK;
|
||||
if (ggms != 0) {
|
||||
ggms = 1 << ggms;
|
||||
ggms = 1ULL << ggms;
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,83 +423,13 @@ static const MemoryRegionOps vfio_igd_index_quirk = {
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static uint64_t vfio_igd_pci_config_read(VFIOPCIDevice *vdev, uint64_t offset,
|
||||
unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
return pci_get_byte(vdev->pdev.config + offset);
|
||||
case 2:
|
||||
return pci_get_word(vdev->pdev.config + offset);
|
||||
case 4:
|
||||
return pci_get_long(vdev->pdev.config + offset);
|
||||
case 8:
|
||||
return pci_get_quad(vdev->pdev.config + offset);
|
||||
default:
|
||||
hw_error("igd: unsupported pci config read at %"PRIx64", size %u",
|
||||
offset, size);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vfio_igd_pci_config_write(VFIOPCIDevice *vdev, uint64_t offset,
|
||||
uint64_t data, unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
pci_set_byte(vdev->pdev.config + offset, data);
|
||||
break;
|
||||
case 2:
|
||||
pci_set_word(vdev->pdev.config + offset, data);
|
||||
break;
|
||||
case 4:
|
||||
pci_set_long(vdev->pdev.config + offset, data);
|
||||
break;
|
||||
case 8:
|
||||
pci_set_quad(vdev->pdev.config + offset, data);
|
||||
break;
|
||||
default:
|
||||
hw_error("igd: unsupported pci config write at %"PRIx64", size %u",
|
||||
offset, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define VFIO_IGD_QUIRK_MIRROR_REG(reg, name) \
|
||||
static uint64_t vfio_igd_quirk_read_##name(void *opaque, \
|
||||
hwaddr addr, unsigned size) \
|
||||
{ \
|
||||
VFIOPCIDevice *vdev = opaque; \
|
||||
\
|
||||
return vfio_igd_pci_config_read(vdev, reg + addr, size); \
|
||||
} \
|
||||
\
|
||||
static void vfio_igd_quirk_write_##name(void *opaque, hwaddr addr, \
|
||||
uint64_t data, unsigned size) \
|
||||
{ \
|
||||
VFIOPCIDevice *vdev = opaque; \
|
||||
\
|
||||
vfio_igd_pci_config_write(vdev, reg + addr, data, size); \
|
||||
} \
|
||||
\
|
||||
static const MemoryRegionOps vfio_igd_quirk_mirror_##name = { \
|
||||
.read = vfio_igd_quirk_read_##name, \
|
||||
.write = vfio_igd_quirk_write_##name, \
|
||||
.endianness = DEVICE_LITTLE_ENDIAN, \
|
||||
};
|
||||
|
||||
VFIO_IGD_QUIRK_MIRROR_REG(IGD_GMCH, ggc)
|
||||
VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM, bdsm)
|
||||
VFIO_IGD_QUIRK_MIRROR_REG(IGD_BDSM_GEN11, bdsm64)
|
||||
|
||||
#define IGD_GGC_MMIO_OFFSET 0x108040
|
||||
#define IGD_BDSM_MMIO_OFFSET 0x1080C0
|
||||
|
||||
void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
|
||||
{
|
||||
VFIOQuirk *quirk;
|
||||
VFIOQuirk *ggc_quirk, *bdsm_quirk;
|
||||
VFIOConfigMirrorQuirk *ggc_mirror, *bdsm_mirror;
|
||||
int gen;
|
||||
|
||||
/*
|
||||
@ -522,33 +453,39 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
|
||||
return;
|
||||
}
|
||||
|
||||
quirk = vfio_quirk_alloc(2);
|
||||
quirk->data = vdev;
|
||||
ggc_quirk = vfio_quirk_alloc(1);
|
||||
ggc_mirror = ggc_quirk->data = g_malloc0(sizeof(*ggc_mirror));
|
||||
ggc_mirror->mem = ggc_quirk->mem;
|
||||
ggc_mirror->vdev = vdev;
|
||||
ggc_mirror->bar = nr;
|
||||
ggc_mirror->offset = IGD_GGC_MMIO_OFFSET;
|
||||
ggc_mirror->config_offset = IGD_GMCH;
|
||||
|
||||
memory_region_init_io(&quirk->mem[0], OBJECT(vdev),
|
||||
&vfio_igd_quirk_mirror_ggc, vdev,
|
||||
memory_region_init_io(ggc_mirror->mem, OBJECT(vdev),
|
||||
&vfio_generic_mirror_quirk, ggc_mirror,
|
||||
"vfio-igd-ggc-quirk", 2);
|
||||
memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
|
||||
IGD_GGC_MMIO_OFFSET, &quirk->mem[0],
|
||||
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
|
||||
ggc_mirror->offset, ggc_mirror->mem,
|
||||
1);
|
||||
|
||||
if (gen < 11) {
|
||||
memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
|
||||
&vfio_igd_quirk_mirror_bdsm, vdev,
|
||||
"vfio-igd-bdsm-quirk", 4);
|
||||
memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
|
||||
IGD_BDSM_MMIO_OFFSET,
|
||||
&quirk->mem[1], 1);
|
||||
} else {
|
||||
memory_region_init_io(&quirk->mem[1], OBJECT(vdev),
|
||||
&vfio_igd_quirk_mirror_bdsm64, vdev,
|
||||
"vfio-igd-bdsm-quirk", 8);
|
||||
memory_region_add_subregion_overlap(vdev->bars[0].region.mem,
|
||||
IGD_BDSM_MMIO_OFFSET,
|
||||
&quirk->mem[1], 1);
|
||||
}
|
||||
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, ggc_quirk, next);
|
||||
|
||||
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
|
||||
bdsm_quirk = vfio_quirk_alloc(1);
|
||||
bdsm_mirror = bdsm_quirk->data = g_malloc0(sizeof(*bdsm_mirror));
|
||||
bdsm_mirror->mem = bdsm_quirk->mem;
|
||||
bdsm_mirror->vdev = vdev;
|
||||
bdsm_mirror->bar = nr;
|
||||
bdsm_mirror->offset = IGD_BDSM_MMIO_OFFSET;
|
||||
bdsm_mirror->config_offset = (gen < 11) ? IGD_BDSM : IGD_BDSM_GEN11;
|
||||
|
||||
memory_region_init_io(bdsm_mirror->mem, OBJECT(vdev),
|
||||
&vfio_generic_mirror_quirk, bdsm_mirror,
|
||||
"vfio-igd-bdsm-quirk", (gen < 11) ? 4 : 8);
|
||||
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
|
||||
bdsm_mirror->offset, bdsm_mirror->mem,
|
||||
1);
|
||||
|
||||
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, bdsm_quirk, next);
|
||||
}
|
||||
|
||||
void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
|
||||
|
@ -515,8 +515,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
|
||||
} else {
|
||||
ret = iommufd_cdev_ram_block_discard_disable(true);
|
||||
if (ret) {
|
||||
error_setg(errp,
|
||||
"Cannot set discarding of RAM broken (%d)", ret);
|
||||
error_setg_errno(errp, -ret,
|
||||
"Cannot set discarding of RAM broken");
|
||||
goto err_discard_disable;
|
||||
}
|
||||
goto found_container;
|
||||
@ -544,6 +544,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev,
|
||||
|
||||
ret = iommufd_cdev_ram_block_discard_disable(true);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken");
|
||||
goto err_discard_disable;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "hw/nvram/fw_cfg.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "pci.h"
|
||||
#include "pci-quirks.h"
|
||||
#include "trace.h"
|
||||
|
||||
/*
|
||||
@ -66,40 +67,6 @@ bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev)
|
||||
* Device specific region quirks (mostly backdoors to PCI config space)
|
||||
*/
|
||||
|
||||
/*
|
||||
* The generic window quirks operate on an address and data register,
|
||||
* vfio_generic_window_address_quirk handles the address register and
|
||||
* vfio_generic_window_data_quirk handles the data register. These ops
|
||||
* pass reads and writes through to hardware until a value matching the
|
||||
* stored address match/mask is written. When this occurs, the data
|
||||
* register access emulated PCI config space for the device rather than
|
||||
* passing through accesses. This enables devices where PCI config space
|
||||
* is accessible behind a window register to maintain the virtualization
|
||||
* provided through vfio.
|
||||
*/
|
||||
typedef struct VFIOConfigWindowMatch {
|
||||
uint32_t match;
|
||||
uint32_t mask;
|
||||
} VFIOConfigWindowMatch;
|
||||
|
||||
typedef struct VFIOConfigWindowQuirk {
|
||||
struct VFIOPCIDevice *vdev;
|
||||
|
||||
uint32_t address_val;
|
||||
|
||||
uint32_t address_offset;
|
||||
uint32_t data_offset;
|
||||
|
||||
bool window_enabled;
|
||||
uint8_t bar;
|
||||
|
||||
MemoryRegion *addr_mem;
|
||||
MemoryRegion *data_mem;
|
||||
|
||||
uint32_t nr_matches;
|
||||
VFIOConfigWindowMatch matches[];
|
||||
} VFIOConfigWindowQuirk;
|
||||
|
||||
static uint64_t vfio_generic_window_quirk_address_read(void *opaque,
|
||||
hwaddr addr,
|
||||
unsigned size)
|
||||
@ -135,7 +102,7 @@ static void vfio_generic_window_quirk_address_write(void *opaque, hwaddr addr,
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps vfio_generic_window_address_quirk = {
|
||||
const MemoryRegionOps vfio_generic_window_address_quirk = {
|
||||
.read = vfio_generic_window_quirk_address_read,
|
||||
.write = vfio_generic_window_quirk_address_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
@ -178,26 +145,12 @@ static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr,
|
||||
addr + window->data_offset, data, size);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps vfio_generic_window_data_quirk = {
|
||||
const MemoryRegionOps vfio_generic_window_data_quirk = {
|
||||
.read = vfio_generic_window_quirk_data_read,
|
||||
.write = vfio_generic_window_quirk_data_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
/*
|
||||
* The generic mirror quirk handles devices which expose PCI config space
|
||||
* through a region within a BAR. When enabled, reads and writes are
|
||||
* redirected through to emulated PCI config space. XXX if PCI config space
|
||||
* used memory regions, this could just be an alias.
|
||||
*/
|
||||
typedef struct VFIOConfigMirrorQuirk {
|
||||
struct VFIOPCIDevice *vdev;
|
||||
uint32_t offset;
|
||||
uint8_t bar;
|
||||
MemoryRegion *mem;
|
||||
uint8_t data[];
|
||||
} VFIOConfigMirrorQuirk;
|
||||
|
||||
static uint64_t vfio_generic_quirk_mirror_read(void *opaque,
|
||||
hwaddr addr, unsigned size)
|
||||
{
|
||||
@ -209,6 +162,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque,
|
||||
(void)vfio_region_read(&vdev->bars[mirror->bar].region,
|
||||
addr + mirror->offset, size);
|
||||
|
||||
addr += mirror->config_offset;
|
||||
data = vfio_pci_read_config(&vdev->pdev, addr, size);
|
||||
trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name,
|
||||
memory_region_name(mirror->mem),
|
||||
@ -222,13 +176,14 @@ static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr,
|
||||
VFIOConfigMirrorQuirk *mirror = opaque;
|
||||
VFIOPCIDevice *vdev = mirror->vdev;
|
||||
|
||||
addr += mirror->config_offset;
|
||||
vfio_pci_write_config(&vdev->pdev, addr, data, size);
|
||||
trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name,
|
||||
memory_region_name(mirror->mem),
|
||||
addr, data);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps vfio_generic_mirror_quirk = {
|
||||
const MemoryRegionOps vfio_generic_mirror_quirk = {
|
||||
.read = vfio_generic_quirk_mirror_read,
|
||||
.write = vfio_generic_quirk_mirror_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
|
72
hw/vfio/pci-quirks.h
Normal file
72
hw/vfio/pci-quirks.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* vfio generic region quirks (mostly backdoors to PCI config space)
|
||||
*
|
||||
* Copyright Red Hat, Inc. 2012-2015
|
||||
*
|
||||
* Authors:
|
||||
* Alex Williamson <alex.williamson@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||
* the COPYING file in the top-level directory.
|
||||
*/
|
||||
#ifndef HW_VFIO_VFIO_PCI_QUIRKS_H
|
||||
#define HW_VFIO_VFIO_PCI_QUIRKS_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "exec/memop.h"
|
||||
|
||||
/*
|
||||
* The generic window quirks operate on an address and data register,
|
||||
* vfio_generic_window_address_quirk handles the address register and
|
||||
* vfio_generic_window_data_quirk handles the data register. These ops
|
||||
* pass reads and writes through to hardware until a value matching the
|
||||
* stored address match/mask is written. When this occurs, the data
|
||||
* register access emulated PCI config space for the device rather than
|
||||
* passing through accesses. This enables devices where PCI config space
|
||||
* is accessible behind a window register to maintain the virtualization
|
||||
* provided through vfio.
|
||||
*/
|
||||
typedef struct VFIOConfigWindowMatch {
|
||||
uint32_t match;
|
||||
uint32_t mask;
|
||||
} VFIOConfigWindowMatch;
|
||||
|
||||
typedef struct VFIOConfigWindowQuirk {
|
||||
struct VFIOPCIDevice *vdev;
|
||||
|
||||
uint32_t address_val;
|
||||
|
||||
uint32_t address_offset;
|
||||
uint32_t data_offset;
|
||||
|
||||
bool window_enabled;
|
||||
uint8_t bar;
|
||||
|
||||
MemoryRegion *addr_mem;
|
||||
MemoryRegion *data_mem;
|
||||
|
||||
uint32_t nr_matches;
|
||||
VFIOConfigWindowMatch matches[];
|
||||
} VFIOConfigWindowQuirk;
|
||||
|
||||
extern const MemoryRegionOps vfio_generic_window_address_quirk;
|
||||
extern const MemoryRegionOps vfio_generic_window_data_quirk;
|
||||
|
||||
/*
|
||||
* The generic mirror quirk handles devices which expose PCI config space
|
||||
* through a region within a BAR. When enabled, reads and writes are
|
||||
* redirected through to emulated PCI config space. XXX if PCI config space
|
||||
* used memory regions, this could just be an alias.
|
||||
*/
|
||||
typedef struct VFIOConfigMirrorQuirk {
|
||||
struct VFIOPCIDevice *vdev;
|
||||
uint32_t offset; /* Offset in BAR */
|
||||
uint32_t config_offset; /* Offset in PCI config space */
|
||||
uint8_t bar;
|
||||
MemoryRegion *mem;
|
||||
uint8_t data[];
|
||||
} VFIOConfigMirrorQuirk;
|
||||
|
||||
extern const MemoryRegionOps vfio_generic_mirror_quirk;
|
||||
|
||||
#endif /* HW_VFIO_VFIO_PCI_QUIRKS_H */
|
@ -3116,7 +3116,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
|
||||
|
||||
if (!vbasedev->mdev &&
|
||||
!pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) {
|
||||
error_prepend(errp, "Failed to set iommu_device: ");
|
||||
error_prepend(errp, "Failed to set vIOMMU: ");
|
||||
goto out_teardown;
|
||||
}
|
||||
|
||||
|
@ -252,6 +252,7 @@ bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp);
|
||||
bool vfio_attach_device(char *name, VFIODevice *vbasedev,
|
||||
AddressSpace *as, Error **errp);
|
||||
void vfio_detach_device(VFIODevice *vbasedev);
|
||||
VFIODevice *vfio_get_vfio_device(Object *obj);
|
||||
|
||||
int vfio_kvm_device_add_fd(int fd, Error **errp);
|
||||
int vfio_kvm_device_del_fd(int fd, Error **errp);
|
||||
|
@ -466,6 +466,18 @@ void warn_reportf_err(Error *err, const char *fmt, ...)
|
||||
void error_reportf_err(Error *err, const char *fmt, ...)
|
||||
G_GNUC_PRINTF(2, 3);
|
||||
|
||||
/*
|
||||
* Similar to warn_report_err(), except it prints the message just once.
|
||||
* Return true when it prints, false otherwise.
|
||||
*/
|
||||
bool warn_report_err_once_cond(bool *printed, Error *err);
|
||||
|
||||
#define warn_report_err_once(err) \
|
||||
({ \
|
||||
static bool print_once_; \
|
||||
warn_report_err_once_cond(&print_once_, err); \
|
||||
})
|
||||
|
||||
/*
|
||||
* Just like error_setg(), except you get to specify the error class.
|
||||
* Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is
|
||||
|
11
util/error.c
11
util/error.c
@ -247,6 +247,17 @@ void warn_report_err(Error *err)
|
||||
error_free(err);
|
||||
}
|
||||
|
||||
bool warn_report_err_once_cond(bool *printed, Error *err)
|
||||
{
|
||||
if (*printed) {
|
||||
error_free(err);
|
||||
return false;
|
||||
}
|
||||
*printed = true;
|
||||
warn_report_err(err);
|
||||
return true;
|
||||
}
|
||||
|
||||
void error_reportf_err(Error *err, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
Loading…
x
Reference in New Issue
Block a user