224 lines
7 KiB
C
224 lines
7 KiB
C
/*
|
|
* vfio based device assignment support - PCI devices
|
|
*
|
|
* 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_H
|
|
#define HW_VFIO_VFIO_PCI_H
|
|
|
|
#include "exec/memory.h"
|
|
#include "hw/pci/pci.h"
|
|
#include "hw/vfio/vfio-common.h"
|
|
#include "qemu/event_notifier.h"
|
|
#include "qemu/queue.h"
|
|
#include "qemu/timer.h"
|
|
|
|
#define PCI_ANY_ID (~0)
|
|
|
|
struct VFIOPCIDevice;
|
|
|
|
typedef struct VFIOIOEventFD {
|
|
QLIST_ENTRY(VFIOIOEventFD) next;
|
|
MemoryRegion *mr;
|
|
hwaddr addr;
|
|
unsigned size;
|
|
uint64_t data;
|
|
EventNotifier e;
|
|
VFIORegion *region;
|
|
hwaddr region_addr;
|
|
bool dynamic; /* Added runtime, removed on device reset */
|
|
bool vfio;
|
|
} VFIOIOEventFD;
|
|
|
|
typedef struct VFIOQuirk {
|
|
QLIST_ENTRY(VFIOQuirk) next;
|
|
void *data;
|
|
QLIST_HEAD(, VFIOIOEventFD) ioeventfds;
|
|
int nr_mem;
|
|
MemoryRegion *mem;
|
|
void (*reset)(struct VFIOPCIDevice *vdev, struct VFIOQuirk *quirk);
|
|
} VFIOQuirk;
|
|
|
|
typedef struct VFIOBAR {
|
|
VFIORegion region;
|
|
MemoryRegion *mr;
|
|
size_t size;
|
|
uint8_t type;
|
|
bool ioport;
|
|
bool mem64;
|
|
QLIST_HEAD(, VFIOQuirk) quirks;
|
|
} VFIOBAR;
|
|
|
|
typedef struct VFIOVGARegion {
|
|
MemoryRegion mem;
|
|
off_t offset;
|
|
int nr;
|
|
QLIST_HEAD(, VFIOQuirk) quirks;
|
|
} VFIOVGARegion;
|
|
|
|
typedef struct VFIOVGA {
|
|
off_t fd_offset;
|
|
int fd;
|
|
VFIOVGARegion region[QEMU_PCI_VGA_NUM_REGIONS];
|
|
} VFIOVGA;
|
|
|
|
typedef struct VFIOINTx {
|
|
bool pending; /* interrupt pending */
|
|
bool kvm_accel; /* set when QEMU bypass through KVM enabled */
|
|
uint8_t pin; /* which pin to pull for qemu_set_irq */
|
|
EventNotifier interrupt; /* eventfd triggered on interrupt */
|
|
EventNotifier unmask; /* eventfd for unmask on QEMU bypass */
|
|
PCIINTxRoute route; /* routing info for QEMU bypass */
|
|
uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */
|
|
QEMUTimer *mmap_timer; /* enable mmaps after periods w/o interrupts */
|
|
} VFIOINTx;
|
|
|
|
typedef struct VFIOMSIVector {
|
|
/*
|
|
* Two interrupt paths are configured per vector. The first, is only used
|
|
* for interrupts injected via QEMU. This is typically the non-accel path,
|
|
* but may also be used when we want QEMU to handle masking and pending
|
|
* bits. The KVM path bypasses QEMU and is therefore higher performance,
|
|
* but requires masking at the device. virq is used to track the MSI route
|
|
* through KVM, thus kvm_interrupt is only available when virq is set to a
|
|
* valid (>= 0) value.
|
|
*/
|
|
EventNotifier interrupt;
|
|
EventNotifier kvm_interrupt;
|
|
struct VFIOPCIDevice *vdev; /* back pointer to device */
|
|
int virq;
|
|
bool use;
|
|
} VFIOMSIVector;
|
|
|
|
enum {
|
|
VFIO_INT_NONE = 0,
|
|
VFIO_INT_INTx = 1,
|
|
VFIO_INT_MSI = 2,
|
|
VFIO_INT_MSIX = 3,
|
|
};
|
|
|
|
/* Cache of MSI-X setup */
|
|
typedef struct VFIOMSIXInfo {
|
|
uint8_t table_bar;
|
|
uint8_t pba_bar;
|
|
uint16_t entries;
|
|
uint32_t table_offset;
|
|
uint32_t pba_offset;
|
|
unsigned long *pending;
|
|
} VFIOMSIXInfo;
|
|
|
|
typedef struct VFIOPCIDevice {
|
|
PCIDevice pdev;
|
|
VFIODevice vbasedev;
|
|
VFIOINTx intx;
|
|
unsigned int config_size;
|
|
uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */
|
|
off_t config_offset; /* Offset of config space region within device fd */
|
|
unsigned int rom_size;
|
|
off_t rom_offset; /* Offset of ROM region within device fd */
|
|
void *rom;
|
|
int msi_cap_size;
|
|
VFIOMSIVector *msi_vectors;
|
|
VFIOMSIXInfo *msix;
|
|
int nr_vectors; /* Number of MSI/MSIX vectors currently in use */
|
|
int interrupt; /* Current interrupt type */
|
|
VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */
|
|
VFIOVGA *vga; /* 0xa0000, 0x3b0, 0x3c0 */
|
|
void *igd_opregion;
|
|
PCIHostDeviceAddress host;
|
|
EventNotifier err_notifier;
|
|
EventNotifier req_notifier;
|
|
int (*resetfn)(struct VFIOPCIDevice *);
|
|
uint32_t vendor_id;
|
|
uint32_t device_id;
|
|
uint32_t sub_vendor_id;
|
|
uint32_t sub_device_id;
|
|
uint32_t features;
|
|
#define VFIO_FEATURE_ENABLE_VGA_BIT 0
|
|
#define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT)
|
|
#define VFIO_FEATURE_ENABLE_REQ_BIT 1
|
|
#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT)
|
|
#define VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT 2
|
|
#define VFIO_FEATURE_ENABLE_IGD_OPREGION \
|
|
(1 << VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT)
|
|
OnOffAuto display;
|
|
uint32_t display_xres;
|
|
uint32_t display_yres;
|
|
int32_t bootindex;
|
|
uint32_t igd_gms;
|
|
OffAutoPCIBAR msix_relo;
|
|
uint8_t pm_cap;
|
|
uint8_t nv_gpudirect_clique;
|
|
bool pci_aer;
|
|
bool req_enabled;
|
|
bool has_flr;
|
|
bool has_pm_reset;
|
|
bool rom_read_failed;
|
|
bool no_kvm_intx;
|
|
bool no_kvm_msi;
|
|
bool no_kvm_msix;
|
|
bool no_geforce_quirks;
|
|
bool no_kvm_ioeventfd;
|
|
bool no_vfio_ioeventfd;
|
|
bool enable_ramfb;
|
|
VFIODisplay *dpy;
|
|
Error *migration_blocker;
|
|
Notifier irqchip_change_notifier;
|
|
} VFIOPCIDevice;
|
|
|
|
/* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */
|
|
static inline bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t device)
|
|
{
|
|
return (vendor == PCI_ANY_ID || vendor == vdev->vendor_id) &&
|
|
(device == PCI_ANY_ID || device == vdev->device_id);
|
|
}
|
|
|
|
static inline bool vfio_is_vga(VFIOPCIDevice *vdev)
|
|
{
|
|
PCIDevice *pdev = &vdev->pdev;
|
|
uint16_t class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
|
|
|
|
return class == PCI_CLASS_DISPLAY_VGA;
|
|
}
|
|
|
|
uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len);
|
|
void vfio_pci_write_config(PCIDevice *pdev,
|
|
uint32_t addr, uint32_t val, int len);
|
|
|
|
uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size);
|
|
void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size);
|
|
|
|
bool vfio_blacklist_opt_rom(VFIOPCIDevice *vdev);
|
|
void vfio_vga_quirk_setup(VFIOPCIDevice *vdev);
|
|
void vfio_vga_quirk_exit(VFIOPCIDevice *vdev);
|
|
void vfio_vga_quirk_finalize(VFIOPCIDevice *vdev);
|
|
void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr);
|
|
void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr);
|
|
void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr);
|
|
void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
|
|
int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp);
|
|
void vfio_quirk_reset(VFIOPCIDevice *vdev);
|
|
VFIOQuirk *vfio_quirk_alloc(int nr_mem);
|
|
void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr);
|
|
|
|
extern const PropertyInfo qdev_prop_nv_gpudirect_clique;
|
|
|
|
int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp);
|
|
|
|
int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
|
|
struct vfio_region_info *info,
|
|
Error **errp);
|
|
int vfio_pci_nvidia_v100_ram_init(VFIOPCIDevice *vdev, Error **errp);
|
|
int vfio_pci_nvlink2_init(VFIOPCIDevice *vdev, Error **errp);
|
|
|
|
void vfio_display_reset(VFIOPCIDevice *vdev);
|
|
int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp);
|
|
void vfio_display_finalize(VFIOPCIDevice *vdev);
|
|
|
|
#endif /* HW_VFIO_VFIO_PCI_H */
|