VFIO updates 2016-10-17
- Convert to realize & improve error reporting (Eric Auger) - RTL quirk bug fix (Thorsten Kohfeldt) - Skip duplicate pre/post reset (Cao jin) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJYBSm0AAoJECObm247sIsi6/UP/jXTErBSjQ+m+Mfgc/GIEBhH GFYpO6gI5mgkOlscgmzlga/WyV2TuhnL+zSFmuMiGix4PyTOhkFrW9gzc5dWf5qe pIxPjrRC4uHX77Kyxsi623RAmltxYe+SPfh4v/3ZpYZQFXjFX6aDMyTWVedrGeXX AXW8L4L7ngLqoW77Ak6QKHh/ehzuCdLbNe/e+FwEsly4dnajhEzUkxqqY6P2Dufe lfkmtOTnu/mTjEY3VTrSmcdhHsYBs11Rk4SZbSHBg2B12Ftf96bFpH5E0KXvPY49 ywoM36nbOBkhibZXjI7lDp1lkGSNVO+nlPP7060QubiEMltCzwQK/XdZeL76NGy0 HK7llJivX6f6kMI349HFsDXOu8gNa9RsuV88Z6EQQoRUdbiipP++QtbSS6kufQgL 6Xxu/ElrxeAmoM0uhUMYnskNL00ZrIGNQco0tXpo7vQZ59AcPbJYQvQi36HBOW5B 4e5I+X7+ULugUKP0vpQwJ/9pN6NFq8nJuo4YtLRbOj0DaE+tpBdxj7J06QsfdgcP i/itvfulzCDERYn2kAlC0yE6AJ3vqeufsWUaCzPfCan/xHeOxleLTvGEytRNRkhK g831TPLQbv2BdFeEE4DEpc6haizHhsKPMMEn4mQb8ztQM99hyn4WYmzyHBYyqIMl nWBI+FzGt45eTR/snau0 =VVuQ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/awilliam/tags/vfio-updates-20161017.0' into staging VFIO updates 2016-10-17 - Convert to realize & improve error reporting (Eric Auger) - RTL quirk bug fix (Thorsten Kohfeldt) - Skip duplicate pre/post reset (Cao jin) # gpg: Signature made Mon 17 Oct 2016 20:42:44 BST # gpg: using RSA key 0x239B9B6E3BB08B22 # gpg: Good signature from "Alex Williamson <alex.williamson@redhat.com>" # gpg: aka "Alex Williamson <alex@shazbot.org>" # gpg: aka "Alex Williamson <alwillia@redhat.com>" # gpg: aka "Alex Williamson <alex.l.williamson@gmail.com>" # Primary key fingerprint: 42F6 C04E 540B D1A9 9E7B 8A90 239B 9B6E 3BB0 8B22 * remotes/awilliam/tags/vfio-updates-20161017.0: vfio: fix duplicate function call vfio/pci: Fix vfio_rtl8168_quirk_data_read address offset vfio/pci: Handle host oversight vfio/pci: Remove vfio_populate_device returned value vfio/pci: Remove vfio_msix_early_setup returned value vfio/pci: Conversion to realize vfio/platform: Pass an error object to vfio_base_device_init vfio/platform: fix a wrong returned value in vfio_populate_device vfio/platform: Pass an error object to vfio_populate_device vfio: Pass an error object to vfio_get_device vfio: Pass an error object to vfio_get_group vfio: Pass an Error object to vfio_connect_container vfio/pci: Pass an error object to vfio_pci_igd_opregion_init vfio/pci: Pass an error object to vfio_add_capabilities vfio/pci: Pass an error object to vfio_intx_enable vfio/pci: Pass an error object to vfio_msix_early_setup vfio/pci: Pass an error object to vfio_populate_device vfio/pci: Pass an error object to vfio_populate_vga vfio/pci: Use local error object in vfio_initfn Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1b0d3845b4
@ -34,6 +34,7 @@
|
|||||||
#include "qemu/range.h"
|
#include "qemu/range.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
struct vfio_group_head vfio_group_list =
|
struct vfio_group_head vfio_group_list =
|
||||||
QLIST_HEAD_INITIALIZER(vfio_group_list);
|
QLIST_HEAD_INITIALIZER(vfio_group_list);
|
||||||
@ -900,7 +901,8 @@ static void vfio_put_address_space(VFIOAddressSpace *space)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
static int vfio_connect_container(VFIOGroup *group, AddressSpace *as,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
VFIOContainer *container;
|
VFIOContainer *container;
|
||||||
int ret, fd;
|
int ret, fd;
|
||||||
@ -918,15 +920,15 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
|
|
||||||
fd = qemu_open("/dev/vfio/vfio", O_RDWR);
|
fd = qemu_open("/dev/vfio/vfio", O_RDWR);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
error_report("vfio: failed to open /dev/vfio/vfio: %m");
|
error_setg_errno(errp, errno, "failed to open /dev/vfio/vfio");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto put_space_exit;
|
goto put_space_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ioctl(fd, VFIO_GET_API_VERSION);
|
ret = ioctl(fd, VFIO_GET_API_VERSION);
|
||||||
if (ret != VFIO_API_VERSION) {
|
if (ret != VFIO_API_VERSION) {
|
||||||
error_report("vfio: supported vfio version: %d, "
|
error_setg(errp, "supported vfio version: %d, "
|
||||||
"reported version: %d", VFIO_API_VERSION, ret);
|
"reported version: %d", VFIO_API_VERSION, ret);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto close_fd_exit;
|
goto close_fd_exit;
|
||||||
}
|
}
|
||||||
@ -941,7 +943,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
|
|
||||||
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
|
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to set group container: %m");
|
error_setg_errno(errp, errno, "failed to set group container");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto free_container_exit;
|
goto free_container_exit;
|
||||||
}
|
}
|
||||||
@ -949,7 +951,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU;
|
container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU;
|
||||||
ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
|
ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to set iommu for container: %m");
|
error_setg_errno(errp, errno, "failed to set iommu for container");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto free_container_exit;
|
goto free_container_exit;
|
||||||
}
|
}
|
||||||
@ -976,7 +978,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
|
|
||||||
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
|
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to set group container: %m");
|
error_setg_errno(errp, errno, "failed to set group container");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto free_container_exit;
|
goto free_container_exit;
|
||||||
}
|
}
|
||||||
@ -984,7 +986,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
v2 ? VFIO_SPAPR_TCE_v2_IOMMU : VFIO_SPAPR_TCE_IOMMU;
|
v2 ? VFIO_SPAPR_TCE_v2_IOMMU : VFIO_SPAPR_TCE_IOMMU;
|
||||||
ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
|
ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to set iommu for container: %m");
|
error_setg_errno(errp, errno, "failed to set iommu for container");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto free_container_exit;
|
goto free_container_exit;
|
||||||
}
|
}
|
||||||
@ -997,7 +999,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
if (!v2) {
|
if (!v2) {
|
||||||
ret = ioctl(fd, VFIO_IOMMU_ENABLE);
|
ret = ioctl(fd, VFIO_IOMMU_ENABLE);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to enable container: %m");
|
error_setg_errno(errp, errno, "failed to enable container");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
goto free_container_exit;
|
goto free_container_exit;
|
||||||
}
|
}
|
||||||
@ -1008,7 +1010,9 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
&address_space_memory);
|
&address_space_memory);
|
||||||
if (container->error) {
|
if (container->error) {
|
||||||
memory_listener_unregister(&container->prereg_listener);
|
memory_listener_unregister(&container->prereg_listener);
|
||||||
error_report("vfio: RAM memory listener initialization failed for container");
|
ret = container->error;
|
||||||
|
error_setg(errp,
|
||||||
|
"RAM memory listener initialization failed for container");
|
||||||
goto free_container_exit;
|
goto free_container_exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1016,7 +1020,8 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
info.argsz = sizeof(info);
|
info.argsz = sizeof(info);
|
||||||
ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info);
|
ret = ioctl(fd, VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: VFIO_IOMMU_SPAPR_TCE_GET_INFO failed: %m");
|
error_setg_errno(errp, errno,
|
||||||
|
"VFIO_IOMMU_SPAPR_TCE_GET_INFO failed");
|
||||||
ret = -errno;
|
ret = -errno;
|
||||||
if (v2) {
|
if (v2) {
|
||||||
memory_listener_unregister(&container->prereg_listener);
|
memory_listener_unregister(&container->prereg_listener);
|
||||||
@ -1033,6 +1038,8 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
*/
|
*/
|
||||||
ret = vfio_spapr_remove_window(container, info.dma32_window_start);
|
ret = vfio_spapr_remove_window(container, info.dma32_window_start);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
error_setg_errno(errp, -ret,
|
||||||
|
"failed to remove existing window");
|
||||||
goto free_container_exit;
|
goto free_container_exit;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1043,7 +1050,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
0x1000);
|
0x1000);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_report("vfio: No available IOMMU models");
|
error_setg(errp, "No available IOMMU models");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto free_container_exit;
|
goto free_container_exit;
|
||||||
}
|
}
|
||||||
@ -1054,7 +1061,8 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
|||||||
|
|
||||||
if (container->error) {
|
if (container->error) {
|
||||||
ret = container->error;
|
ret = container->error;
|
||||||
error_report("vfio: memory listener initialization failed for container");
|
error_setg_errno(errp, -ret,
|
||||||
|
"memory listener initialization failed for container");
|
||||||
goto listener_release_exit;
|
goto listener_release_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1115,7 +1123,7 @@ static void vfio_disconnect_container(VFIOGroup *group)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VFIOGroup *vfio_get_group(int groupid, AddressSpace *as)
|
VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp)
|
||||||
{
|
{
|
||||||
VFIOGroup *group;
|
VFIOGroup *group;
|
||||||
char path[32];
|
char path[32];
|
||||||
@ -1127,8 +1135,8 @@ VFIOGroup *vfio_get_group(int groupid, AddressSpace *as)
|
|||||||
if (group->container->space->as == as) {
|
if (group->container->space->as == as) {
|
||||||
return group;
|
return group;
|
||||||
} else {
|
} else {
|
||||||
error_report("vfio: group %d used in multiple address spaces",
|
error_setg(errp, "group %d used in multiple address spaces",
|
||||||
group->groupid);
|
group->groupid);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1139,27 +1147,29 @@ VFIOGroup *vfio_get_group(int groupid, AddressSpace *as)
|
|||||||
snprintf(path, sizeof(path), "/dev/vfio/%d", groupid);
|
snprintf(path, sizeof(path), "/dev/vfio/%d", groupid);
|
||||||
group->fd = qemu_open(path, O_RDWR);
|
group->fd = qemu_open(path, O_RDWR);
|
||||||
if (group->fd < 0) {
|
if (group->fd < 0) {
|
||||||
error_report("vfio: error opening %s: %m", path);
|
error_setg_errno(errp, errno, "failed to open %s", path);
|
||||||
goto free_group_exit;
|
goto free_group_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) {
|
if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) {
|
||||||
error_report("vfio: error getting group status: %m");
|
error_setg_errno(errp, errno, "failed to get group %d status", groupid);
|
||||||
goto close_fd_exit;
|
goto close_fd_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
|
if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
|
||||||
error_report("vfio: error, group %d is not viable, please ensure "
|
error_setg(errp, "group %d is not viable", groupid);
|
||||||
"all devices within the iommu_group are bound to their "
|
error_append_hint(errp,
|
||||||
"vfio bus driver.", groupid);
|
"Please ensure all devices within the iommu_group "
|
||||||
|
"are bound to their vfio bus driver.\n");
|
||||||
goto close_fd_exit;
|
goto close_fd_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
group->groupid = groupid;
|
group->groupid = groupid;
|
||||||
QLIST_INIT(&group->device_list);
|
QLIST_INIT(&group->device_list);
|
||||||
|
|
||||||
if (vfio_connect_container(group, as)) {
|
if (vfio_connect_container(group, as, errp)) {
|
||||||
error_report("vfio: failed to setup container for group %d", groupid);
|
error_prepend(errp, "failed to setup container for group %d: ",
|
||||||
|
groupid);
|
||||||
goto close_fd_exit;
|
goto close_fd_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1201,23 +1211,24 @@ void vfio_put_group(VFIOGroup *group)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int vfio_get_device(VFIOGroup *group, const char *name,
|
int vfio_get_device(VFIOGroup *group, const char *name,
|
||||||
VFIODevice *vbasedev)
|
VFIODevice *vbasedev, Error **errp)
|
||||||
{
|
{
|
||||||
struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) };
|
struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) };
|
||||||
int ret, fd;
|
int ret, fd;
|
||||||
|
|
||||||
fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name);
|
fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
error_report("vfio: error getting device %s from group %d: %m",
|
error_setg_errno(errp, errno, "error getting device from group %d",
|
||||||
name, group->groupid);
|
group->groupid);
|
||||||
error_printf("Verify all devices in group %d are bound to vfio-<bus> "
|
error_append_hint(errp,
|
||||||
"or pci-stub and not already in use\n", group->groupid);
|
"Verify all devices in group %d are bound to vfio-<bus> "
|
||||||
|
"or pci-stub and not already in use\n", group->groupid);
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ioctl(fd, VFIO_DEVICE_GET_INFO, &dev_info);
|
ret = ioctl(fd, VFIO_DEVICE_GET_INFO, &dev_info);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: error getting device info: %m");
|
error_setg_errno(errp, errno, "error getting device info");
|
||||||
close(fd);
|
close(fd);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -898,7 +898,7 @@ static uint64_t vfio_rtl8168_quirk_data_read(void *opaque,
|
|||||||
{
|
{
|
||||||
VFIOrtl8168Quirk *rtl = opaque;
|
VFIOrtl8168Quirk *rtl = opaque;
|
||||||
VFIOPCIDevice *vdev = rtl->vdev;
|
VFIOPCIDevice *vdev = rtl->vdev;
|
||||||
uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x74, size);
|
uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x70, size);
|
||||||
|
|
||||||
if (rtl->enabled && (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) {
|
if (rtl->enabled && (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) {
|
||||||
hwaddr offset = rtl->addr & 0xfff;
|
hwaddr offset = rtl->addr & 0xfff;
|
||||||
@ -1056,7 +1056,7 @@ typedef struct VFIOIGDQuirk {
|
|||||||
* of the IGD device.
|
* of the IGD device.
|
||||||
*/
|
*/
|
||||||
int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
|
int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
|
||||||
struct vfio_region_info *info)
|
struct vfio_region_info *info, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -1064,7 +1064,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
|
|||||||
ret = pread(vdev->vbasedev.fd, vdev->igd_opregion,
|
ret = pread(vdev->vbasedev.fd, vdev->igd_opregion,
|
||||||
info->size, info->offset);
|
info->size, info->offset);
|
||||||
if (ret != info->size) {
|
if (ret != info->size) {
|
||||||
error_report("vfio: Error reading IGD OpRegion");
|
error_setg(errp, "failed to read IGD OpRegion");
|
||||||
g_free(vdev->igd_opregion);
|
g_free(vdev->igd_opregion);
|
||||||
vdev->igd_opregion = NULL;
|
vdev->igd_opregion = NULL;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -1363,6 +1363,7 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
|
|||||||
uint64_t *bdsm_size;
|
uint64_t *bdsm_size;
|
||||||
uint32_t gmch;
|
uint32_t gmch;
|
||||||
uint16_t cmd_orig, cmd;
|
uint16_t cmd_orig, cmd;
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This must be an Intel VGA device at address 00:02.0 for us to even
|
* This must be an Intel VGA device at address 00:02.0 for us to even
|
||||||
@ -1464,7 +1465,8 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
|
|||||||
* try to enable it. Probably shouldn't be using legacy mode without VGA,
|
* try to enable it. Probably shouldn't be using legacy mode without VGA,
|
||||||
* but also no point in us enabling VGA if disabled in hardware.
|
* but also no point in us enabling VGA if disabled in hardware.
|
||||||
*/
|
*/
|
||||||
if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev)) {
|
if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev, &err)) {
|
||||||
|
error_reportf_err(err, ERR_PREFIX, vdev->vbasedev.name);
|
||||||
error_report("IGD device %s failed to enable VGA access, "
|
error_report("IGD device %s failed to enable VGA access, "
|
||||||
"legacy mode disabled", vdev->vbasedev.name);
|
"legacy mode disabled", vdev->vbasedev.name);
|
||||||
goto out;
|
goto out;
|
||||||
@ -1487,10 +1489,10 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Setup OpRegion access */
|
/* Setup OpRegion access */
|
||||||
ret = vfio_pci_igd_opregion_init(vdev, opregion);
|
ret = vfio_pci_igd_opregion_init(vdev, opregion, &err);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("IGD device %s failed to setup OpRegion, "
|
error_append_hint(&err, "IGD legacy mode disabled\n");
|
||||||
"legacy mode disabled", vdev->vbasedev.name);
|
error_reportf_err(err, ERR_PREFIX, vdev->vbasedev.name);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
279
hw/vfio/pci.c
279
hw/vfio/pci.c
@ -100,7 +100,7 @@ static void vfio_intx_eoi(VFIODevice *vbasedev)
|
|||||||
vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
|
vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev)
|
static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KVM
|
#ifdef CONFIG_KVM
|
||||||
struct kvm_irqfd irqfd = {
|
struct kvm_irqfd irqfd = {
|
||||||
@ -126,7 +126,7 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev)
|
|||||||
|
|
||||||
/* Get an eventfd for resample/unmask */
|
/* Get an eventfd for resample/unmask */
|
||||||
if (event_notifier_init(&vdev->intx.unmask, 0)) {
|
if (event_notifier_init(&vdev->intx.unmask, 0)) {
|
||||||
error_report("vfio: Error: event_notifier_init failed eoi");
|
error_setg(errp, "event_notifier_init failed eoi");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev)
|
|||||||
irqfd.resamplefd = event_notifier_get_fd(&vdev->intx.unmask);
|
irqfd.resamplefd = event_notifier_get_fd(&vdev->intx.unmask);
|
||||||
|
|
||||||
if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) {
|
if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) {
|
||||||
error_report("vfio: Error: Failed to setup resample irqfd: %m");
|
error_setg_errno(errp, errno, "failed to setup resample irqfd");
|
||||||
goto fail_irqfd;
|
goto fail_irqfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev)
|
|||||||
ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set);
|
ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set);
|
||||||
g_free(irq_set);
|
g_free(irq_set);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: Error: Failed to setup INTx unmask fd: %m");
|
error_setg_errno(errp, -ret, "failed to setup INTx unmask fd");
|
||||||
goto fail_vfio;
|
goto fail_vfio;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,6 +222,7 @@ static void vfio_intx_update(PCIDevice *pdev)
|
|||||||
{
|
{
|
||||||
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
|
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
|
||||||
PCIINTxRoute route;
|
PCIINTxRoute route;
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
if (vdev->interrupt != VFIO_INT_INTx) {
|
if (vdev->interrupt != VFIO_INT_INTx) {
|
||||||
return;
|
return;
|
||||||
@ -244,18 +245,22 @@ static void vfio_intx_update(PCIDevice *pdev)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vfio_intx_enable_kvm(vdev);
|
vfio_intx_enable_kvm(vdev, &err);
|
||||||
|
if (err) {
|
||||||
|
error_reportf_err(err, WARN_PREFIX, vdev->vbasedev.name);
|
||||||
|
}
|
||||||
|
|
||||||
/* Re-enable the interrupt in cased we missed an EOI */
|
/* Re-enable the interrupt in cased we missed an EOI */
|
||||||
vfio_intx_eoi(&vdev->vbasedev);
|
vfio_intx_eoi(&vdev->vbasedev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_intx_enable(VFIOPCIDevice *vdev)
|
static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp)
|
||||||
{
|
{
|
||||||
uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1);
|
uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1);
|
||||||
int ret, argsz;
|
int ret, argsz;
|
||||||
struct vfio_irq_set *irq_set;
|
struct vfio_irq_set *irq_set;
|
||||||
int32_t *pfd;
|
int32_t *pfd;
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
if (!pin) {
|
if (!pin) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -279,7 +284,7 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev)
|
|||||||
|
|
||||||
ret = event_notifier_init(&vdev->intx.interrupt, 0);
|
ret = event_notifier_init(&vdev->intx.interrupt, 0);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: Error: event_notifier_init failed");
|
error_setg_errno(errp, -ret, "event_notifier_init failed");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,13 +304,16 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev)
|
|||||||
ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set);
|
ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_SET_IRQS, irq_set);
|
||||||
g_free(irq_set);
|
g_free(irq_set);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: Error: Failed to setup INTx fd: %m");
|
error_setg_errno(errp, -ret, "failed to setup INTx fd");
|
||||||
qemu_set_fd_handler(*pfd, NULL, NULL, vdev);
|
qemu_set_fd_handler(*pfd, NULL, NULL, vdev);
|
||||||
event_notifier_cleanup(&vdev->intx.interrupt);
|
event_notifier_cleanup(&vdev->intx.interrupt);
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
vfio_intx_enable_kvm(vdev);
|
vfio_intx_enable_kvm(vdev, &err);
|
||||||
|
if (err) {
|
||||||
|
error_reportf_err(err, WARN_PREFIX, vdev->vbasedev.name);
|
||||||
|
}
|
||||||
|
|
||||||
vdev->interrupt = VFIO_INT_INTx;
|
vdev->interrupt = VFIO_INT_INTx;
|
||||||
|
|
||||||
@ -707,6 +715,7 @@ retry:
|
|||||||
|
|
||||||
static void vfio_msi_disable_common(VFIOPCIDevice *vdev)
|
static void vfio_msi_disable_common(VFIOPCIDevice *vdev)
|
||||||
{
|
{
|
||||||
|
Error *err = NULL;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < vdev->nr_vectors; i++) {
|
for (i = 0; i < vdev->nr_vectors; i++) {
|
||||||
@ -726,7 +735,10 @@ static void vfio_msi_disable_common(VFIOPCIDevice *vdev)
|
|||||||
vdev->nr_vectors = 0;
|
vdev->nr_vectors = 0;
|
||||||
vdev->interrupt = VFIO_INT_NONE;
|
vdev->interrupt = VFIO_INT_NONE;
|
||||||
|
|
||||||
vfio_intx_enable(vdev);
|
vfio_intx_enable(vdev, &err);
|
||||||
|
if (err) {
|
||||||
|
error_reportf_err(err, ERR_PREFIX, vdev->vbasedev.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_msix_disable(VFIOPCIDevice *vdev)
|
static void vfio_msix_disable(VFIOPCIDevice *vdev)
|
||||||
@ -1168,7 +1180,7 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos)
|
static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
|
||||||
{
|
{
|
||||||
uint16_t ctrl;
|
uint16_t ctrl;
|
||||||
bool msi_64bit, msi_maskbit;
|
bool msi_64bit, msi_maskbit;
|
||||||
@ -1177,6 +1189,7 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos)
|
|||||||
|
|
||||||
if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl),
|
if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl),
|
||||||
vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) {
|
vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) {
|
||||||
|
error_setg_errno(errp, errno, "failed reading MSI PCI_CAP_FLAGS");
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
ctrl = le16_to_cpu(ctrl);
|
ctrl = le16_to_cpu(ctrl);
|
||||||
@ -1192,8 +1205,8 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos)
|
|||||||
if (ret == -ENOTSUP) {
|
if (ret == -ENOTSUP) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
error_prepend(&err, "vfio: msi_init failed: ");
|
error_prepend(&err, "msi_init failed: ");
|
||||||
error_report_err(err);
|
error_propagate(errp, err);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0);
|
vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0);
|
||||||
@ -1277,7 +1290,7 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
|
|||||||
* need to first look for where the MSI-X table lives. So we
|
* need to first look for where the MSI-X table lives. So we
|
||||||
* unfortunately split MSI-X setup across two functions.
|
* unfortunately split MSI-X setup across two functions.
|
||||||
*/
|
*/
|
||||||
static int vfio_msix_early_setup(VFIOPCIDevice *vdev)
|
static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp)
|
||||||
{
|
{
|
||||||
uint8_t pos;
|
uint8_t pos;
|
||||||
uint16_t ctrl;
|
uint16_t ctrl;
|
||||||
@ -1287,22 +1300,25 @@ static int vfio_msix_early_setup(VFIOPCIDevice *vdev)
|
|||||||
|
|
||||||
pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX);
|
pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX);
|
||||||
if (!pos) {
|
if (!pos) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pread(fd, &ctrl, sizeof(ctrl),
|
if (pread(fd, &ctrl, sizeof(ctrl),
|
||||||
vdev->config_offset + pos + PCI_MSIX_FLAGS) != sizeof(ctrl)) {
|
vdev->config_offset + pos + PCI_MSIX_FLAGS) != sizeof(ctrl)) {
|
||||||
return -errno;
|
error_setg_errno(errp, errno, "failed to read PCI MSIX FLAGS");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pread(fd, &table, sizeof(table),
|
if (pread(fd, &table, sizeof(table),
|
||||||
vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) {
|
vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) {
|
||||||
return -errno;
|
error_setg_errno(errp, errno, "failed to read PCI MSIX TABLE");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pread(fd, &pba, sizeof(pba),
|
if (pread(fd, &pba, sizeof(pba),
|
||||||
vdev->config_offset + pos + PCI_MSIX_PBA) != sizeof(pba)) {
|
vdev->config_offset + pos + PCI_MSIX_PBA) != sizeof(pba)) {
|
||||||
return -errno;
|
error_setg_errno(errp, errno, "failed to read PCI MSIX PBA");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl = le16_to_cpu(ctrl);
|
ctrl = le16_to_cpu(ctrl);
|
||||||
@ -1332,10 +1348,10 @@ static int vfio_msix_early_setup(VFIOPCIDevice *vdev)
|
|||||||
(vdev->device_id & 0xff00) == 0x5800) {
|
(vdev->device_id & 0xff00) == 0x5800) {
|
||||||
msix->pba_offset = 0x1000;
|
msix->pba_offset = 0x1000;
|
||||||
} else {
|
} else {
|
||||||
error_report("vfio: Hardware reports invalid configuration, "
|
error_setg(errp, "hardware reports invalid configuration, "
|
||||||
"MSIX PBA outside of specified BAR");
|
"MSIX PBA outside of specified BAR");
|
||||||
g_free(msix);
|
g_free(msix);
|
||||||
return -EINVAL;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1344,11 +1360,9 @@ static int vfio_msix_early_setup(VFIOPCIDevice *vdev)
|
|||||||
vdev->msix = msix;
|
vdev->msix = msix;
|
||||||
|
|
||||||
vfio_pci_fixup_msix_region(vdev);
|
vfio_pci_fixup_msix_region(vdev);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos)
|
static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -1363,7 +1377,7 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos)
|
|||||||
if (ret == -ENOTSUP) {
|
if (ret == -ENOTSUP) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
error_report("vfio: msix_init failed");
|
error_setg(errp, "msix_init failed");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1548,7 +1562,8 @@ static void vfio_add_emulated_long(VFIOPCIDevice *vdev, int pos,
|
|||||||
vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask);
|
vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size)
|
static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
@ -1560,8 +1575,8 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size)
|
|||||||
type != PCI_EXP_TYPE_LEG_END &&
|
type != PCI_EXP_TYPE_LEG_END &&
|
||||||
type != PCI_EXP_TYPE_RC_END) {
|
type != PCI_EXP_TYPE_RC_END) {
|
||||||
|
|
||||||
error_report("vfio: Assignment of PCIe type 0x%x "
|
error_setg(errp, "assignment of PCIe type 0x%x "
|
||||||
"devices is not currently supported", type);
|
"devices is not currently supported", type);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1695,7 +1710,7 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
|
static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp)
|
||||||
{
|
{
|
||||||
PCIDevice *pdev = &vdev->pdev;
|
PCIDevice *pdev = &vdev->pdev;
|
||||||
uint8_t cap_id, next, size;
|
uint8_t cap_id, next, size;
|
||||||
@ -1720,9 +1735,9 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
|
|||||||
* will be changed as we unwind the stack.
|
* will be changed as we unwind the stack.
|
||||||
*/
|
*/
|
||||||
if (next) {
|
if (next) {
|
||||||
ret = vfio_add_std_cap(vdev, next);
|
ret = vfio_add_std_cap(vdev, next, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Begin the rebuild, use QEMU emulated list bits */
|
/* Begin the rebuild, use QEMU emulated list bits */
|
||||||
@ -1736,40 +1751,40 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos)
|
|||||||
|
|
||||||
switch (cap_id) {
|
switch (cap_id) {
|
||||||
case PCI_CAP_ID_MSI:
|
case PCI_CAP_ID_MSI:
|
||||||
ret = vfio_msi_setup(vdev, pos);
|
ret = vfio_msi_setup(vdev, pos, errp);
|
||||||
break;
|
break;
|
||||||
case PCI_CAP_ID_EXP:
|
case PCI_CAP_ID_EXP:
|
||||||
vfio_check_pcie_flr(vdev, pos);
|
vfio_check_pcie_flr(vdev, pos);
|
||||||
ret = vfio_setup_pcie_cap(vdev, pos, size);
|
ret = vfio_setup_pcie_cap(vdev, pos, size, errp);
|
||||||
break;
|
break;
|
||||||
case PCI_CAP_ID_MSIX:
|
case PCI_CAP_ID_MSIX:
|
||||||
ret = vfio_msix_setup(vdev, pos);
|
ret = vfio_msix_setup(vdev, pos, errp);
|
||||||
break;
|
break;
|
||||||
case PCI_CAP_ID_PM:
|
case PCI_CAP_ID_PM:
|
||||||
vfio_check_pm_reset(vdev, pos);
|
vfio_check_pm_reset(vdev, pos);
|
||||||
vdev->pm_cap = pos;
|
vdev->pm_cap = pos;
|
||||||
ret = pci_add_capability(pdev, cap_id, pos, size);
|
ret = pci_add_capability2(pdev, cap_id, pos, size, errp);
|
||||||
break;
|
break;
|
||||||
case PCI_CAP_ID_AF:
|
case PCI_CAP_ID_AF:
|
||||||
vfio_check_af_flr(vdev, pos);
|
vfio_check_af_flr(vdev, pos);
|
||||||
ret = pci_add_capability(pdev, cap_id, pos, size);
|
ret = pci_add_capability2(pdev, cap_id, pos, size, errp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = pci_add_capability(pdev, cap_id, pos, size);
|
ret = pci_add_capability2(pdev, cap_id, pos, size, errp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("vfio: %s Error adding PCI capability "
|
error_prepend(errp,
|
||||||
"0x%x[0x%x]@0x%x: %d", vdev->vbasedev.name,
|
"failed to add PCI capability 0x%x[0x%x]@0x%x: ",
|
||||||
cap_id, size, pos, ret);
|
cap_id, size, pos);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_add_ext_cap(VFIOPCIDevice *vdev)
|
static void vfio_add_ext_cap(VFIOPCIDevice *vdev)
|
||||||
{
|
{
|
||||||
PCIDevice *pdev = &vdev->pdev;
|
PCIDevice *pdev = &vdev->pdev;
|
||||||
uint32_t header;
|
uint32_t header;
|
||||||
@ -1780,7 +1795,7 @@ static int vfio_add_ext_cap(VFIOPCIDevice *vdev)
|
|||||||
/* Only add extended caps if we have them and the guest can see them */
|
/* Only add extended caps if we have them and the guest can see them */
|
||||||
if (!pci_is_express(pdev) || !pci_bus_is_express(pdev->bus) ||
|
if (!pci_is_express(pdev) || !pci_bus_is_express(pdev->bus) ||
|
||||||
!pci_get_long(pdev->config + PCI_CONFIG_SPACE_SIZE)) {
|
!pci_get_long(pdev->config + PCI_CONFIG_SPACE_SIZE)) {
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1845,10 +1860,10 @@ static int vfio_add_ext_cap(VFIOPCIDevice *vdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_free(config);
|
g_free(config);
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_add_capabilities(VFIOPCIDevice *vdev)
|
static int vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp)
|
||||||
{
|
{
|
||||||
PCIDevice *pdev = &vdev->pdev;
|
PCIDevice *pdev = &vdev->pdev;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1858,12 +1873,13 @@ static int vfio_add_capabilities(VFIOPCIDevice *vdev)
|
|||||||
return 0; /* Nothing to add */
|
return 0; /* Nothing to add */
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]);
|
ret = vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST], errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vfio_add_ext_cap(vdev);
|
vfio_add_ext_cap(vdev);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_pci_pre_reset(VFIOPCIDevice *vdev)
|
static void vfio_pci_pre_reset(VFIOPCIDevice *vdev)
|
||||||
@ -1905,7 +1921,12 @@ static void vfio_pci_pre_reset(VFIOPCIDevice *vdev)
|
|||||||
|
|
||||||
static void vfio_pci_post_reset(VFIOPCIDevice *vdev)
|
static void vfio_pci_post_reset(VFIOPCIDevice *vdev)
|
||||||
{
|
{
|
||||||
vfio_intx_enable(vdev);
|
Error *err = NULL;
|
||||||
|
|
||||||
|
vfio_intx_enable(vdev, &err);
|
||||||
|
if (err) {
|
||||||
|
error_reportf_err(err, ERR_PREFIX, vdev->vbasedev.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name)
|
static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name)
|
||||||
@ -1930,7 +1951,9 @@ static int vfio_pci_hot_reset(VFIOPCIDevice *vdev, bool single)
|
|||||||
|
|
||||||
trace_vfio_pci_hot_reset(vdev->vbasedev.name, single ? "one" : "multi");
|
trace_vfio_pci_hot_reset(vdev->vbasedev.name, single ? "one" : "multi");
|
||||||
|
|
||||||
vfio_pci_pre_reset(vdev);
|
if (!single) {
|
||||||
|
vfio_pci_pre_reset(vdev);
|
||||||
|
}
|
||||||
vdev->vbasedev.needs_reset = false;
|
vdev->vbasedev.needs_reset = false;
|
||||||
|
|
||||||
info = g_malloc0(sizeof(*info));
|
info = g_malloc0(sizeof(*info));
|
||||||
@ -2088,7 +2111,9 @@ out:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out_single:
|
out_single:
|
||||||
vfio_pci_post_reset(vdev);
|
if (!single) {
|
||||||
|
vfio_pci_post_reset(vdev);
|
||||||
|
}
|
||||||
g_free(info);
|
g_free(info);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -2134,7 +2159,7 @@ static VFIODeviceOps vfio_pci_ops = {
|
|||||||
.vfio_eoi = vfio_intx_eoi,
|
.vfio_eoi = vfio_intx_eoi,
|
||||||
};
|
};
|
||||||
|
|
||||||
int vfio_populate_vga(VFIOPCIDevice *vdev)
|
int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp)
|
||||||
{
|
{
|
||||||
VFIODevice *vbasedev = &vdev->vbasedev;
|
VFIODevice *vbasedev = &vdev->vbasedev;
|
||||||
struct vfio_region_info *reg_info;
|
struct vfio_region_info *reg_info;
|
||||||
@ -2142,15 +2167,18 @@ int vfio_populate_vga(VFIOPCIDevice *vdev)
|
|||||||
|
|
||||||
ret = vfio_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, ®_info);
|
ret = vfio_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, ®_info);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
error_setg_errno(errp, -ret,
|
||||||
|
"failed getting region info for VGA region index %d",
|
||||||
|
VFIO_PCI_VGA_REGION_INDEX);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) ||
|
if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) ||
|
||||||
!(reg_info->flags & VFIO_REGION_INFO_FLAG_WRITE) ||
|
!(reg_info->flags & VFIO_REGION_INFO_FLAG_WRITE) ||
|
||||||
reg_info->size < 0xbffff + 1) {
|
reg_info->size < 0xbffff + 1) {
|
||||||
error_report("vfio: Unexpected VGA info, flags 0x%lx, size 0x%lx",
|
error_setg(errp, "unexpected VGA info, flags 0x%lx, size 0x%lx",
|
||||||
(unsigned long)reg_info->flags,
|
(unsigned long)reg_info->flags,
|
||||||
(unsigned long)reg_info->size);
|
(unsigned long)reg_info->size);
|
||||||
g_free(reg_info);
|
g_free(reg_info);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -2199,7 +2227,7 @@ int vfio_populate_vga(VFIOPCIDevice *vdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_populate_device(VFIOPCIDevice *vdev)
|
static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
|
||||||
{
|
{
|
||||||
VFIODevice *vbasedev = &vdev->vbasedev;
|
VFIODevice *vbasedev = &vdev->vbasedev;
|
||||||
struct vfio_region_info *reg_info;
|
struct vfio_region_info *reg_info;
|
||||||
@ -2208,19 +2236,19 @@ static int vfio_populate_device(VFIOPCIDevice *vdev)
|
|||||||
|
|
||||||
/* Sanity check device */
|
/* Sanity check device */
|
||||||
if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PCI)) {
|
if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PCI)) {
|
||||||
error_report("vfio: Um, this isn't a PCI device");
|
error_setg(errp, "this isn't a PCI device");
|
||||||
goto error;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vbasedev->num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) {
|
if (vbasedev->num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) {
|
||||||
error_report("vfio: unexpected number of io regions %u",
|
error_setg(errp, "unexpected number of io regions %u",
|
||||||
vbasedev->num_regions);
|
vbasedev->num_regions);
|
||||||
goto error;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vbasedev->num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1) {
|
if (vbasedev->num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1) {
|
||||||
error_report("vfio: unexpected number of irqs %u", vbasedev->num_irqs);
|
error_setg(errp, "unexpected number of irqs %u", vbasedev->num_irqs);
|
||||||
goto error;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) {
|
for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) {
|
||||||
@ -2231,8 +2259,8 @@ static int vfio_populate_device(VFIOPCIDevice *vdev)
|
|||||||
g_free(name);
|
g_free(name);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: Error getting region %d info: %m", i);
|
error_setg_errno(errp, -ret, "failed to get region %d info", i);
|
||||||
goto error;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_INIT(&vdev->bars[i].quirks);
|
QLIST_INIT(&vdev->bars[i].quirks);
|
||||||
@ -2241,8 +2269,8 @@ static int vfio_populate_device(VFIOPCIDevice *vdev)
|
|||||||
ret = vfio_get_region_info(vbasedev,
|
ret = vfio_get_region_info(vbasedev,
|
||||||
VFIO_PCI_CONFIG_REGION_INDEX, ®_info);
|
VFIO_PCI_CONFIG_REGION_INDEX, ®_info);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: Error getting config info: %m");
|
error_setg_errno(errp, -ret, "failed to get config info");
|
||||||
goto error;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_vfio_populate_device_config(vdev->vbasedev.name,
|
trace_vfio_populate_device_config(vdev->vbasedev.name,
|
||||||
@ -2259,11 +2287,11 @@ static int vfio_populate_device(VFIOPCIDevice *vdev)
|
|||||||
g_free(reg_info);
|
g_free(reg_info);
|
||||||
|
|
||||||
if (vdev->features & VFIO_FEATURE_ENABLE_VGA) {
|
if (vdev->features & VFIO_FEATURE_ENABLE_VGA) {
|
||||||
ret = vfio_populate_vga(vdev);
|
ret = vfio_populate_vga(vdev, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report(
|
error_append_hint(errp, "device does not support "
|
||||||
"vfio: Device does not support requested feature x-vga");
|
"requested feature x-vga\n");
|
||||||
goto error;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2273,17 +2301,13 @@ static int vfio_populate_device(VFIOPCIDevice *vdev)
|
|||||||
if (ret) {
|
if (ret) {
|
||||||
/* This can fail for an old kernel or legacy PCI dev */
|
/* This can fail for an old kernel or legacy PCI dev */
|
||||||
trace_vfio_populate_device_get_irq_info_failure();
|
trace_vfio_populate_device_get_irq_info_failure();
|
||||||
ret = 0;
|
|
||||||
} else if (irq_info.count == 1) {
|
} else if (irq_info.count == 1) {
|
||||||
vdev->pci_aer = true;
|
vdev->pci_aer = true;
|
||||||
} else {
|
} else {
|
||||||
error_report("vfio: %s "
|
error_report(WARN_PREFIX
|
||||||
"Could not enable error recovery for the device",
|
"Could not enable error recovery for the device",
|
||||||
vbasedev->name);
|
vbasedev->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_put_device(VFIOPCIDevice *vdev)
|
static void vfio_put_device(VFIOPCIDevice *vdev)
|
||||||
@ -2487,18 +2511,26 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev)
|
|||||||
vdev->req_enabled = false;
|
vdev->req_enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_initfn(PCIDevice *pdev)
|
static void vfio_realize(PCIDevice *pdev, Error **errp)
|
||||||
{
|
{
|
||||||
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
|
VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev);
|
||||||
VFIODevice *vbasedev_iter;
|
VFIODevice *vbasedev_iter;
|
||||||
VFIOGroup *group;
|
VFIOGroup *group;
|
||||||
char *tmp, group_path[PATH_MAX], *group_name;
|
char *tmp, group_path[PATH_MAX], *group_name;
|
||||||
|
Error *err = NULL;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int groupid;
|
int groupid;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
if (!vdev->vbasedev.sysfsdev) {
|
if (!vdev->vbasedev.sysfsdev) {
|
||||||
|
if (!(~vdev->host.domain || ~vdev->host.bus ||
|
||||||
|
~vdev->host.slot || ~vdev->host.function)) {
|
||||||
|
error_setg(errp, "No provided host device");
|
||||||
|
error_append_hint(errp, "Use -vfio-pci,host=DDDD:BB:DD.F "
|
||||||
|
"or -vfio-pci,sysfsdev=PATH_TO_DEVICE\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
vdev->vbasedev.sysfsdev =
|
vdev->vbasedev.sysfsdev =
|
||||||
g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%01x",
|
g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%01x",
|
||||||
vdev->host.domain, vdev->host.bus,
|
vdev->host.domain, vdev->host.bus,
|
||||||
@ -2506,9 +2538,9 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stat(vdev->vbasedev.sysfsdev, &st) < 0) {
|
if (stat(vdev->vbasedev.sysfsdev, &st) < 0) {
|
||||||
error_report("vfio: error: no such host device: %s",
|
error_setg_errno(errp, errno, "no such host device");
|
||||||
vdev->vbasedev.sysfsdev);
|
error_prepend(errp, ERR_PREFIX, vdev->vbasedev.sysfsdev);
|
||||||
return -errno;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vdev->vbasedev.name = g_strdup(basename(vdev->vbasedev.sysfsdev));
|
vdev->vbasedev.name = g_strdup(basename(vdev->vbasedev.sysfsdev));
|
||||||
@ -2520,45 +2552,44 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
g_free(tmp);
|
g_free(tmp);
|
||||||
|
|
||||||
if (len <= 0 || len >= sizeof(group_path)) {
|
if (len <= 0 || len >= sizeof(group_path)) {
|
||||||
error_report("vfio: error no iommu_group for device");
|
error_setg_errno(errp, len < 0 ? errno : ENAMETOOLONG,
|
||||||
return len < 0 ? -errno : -ENAMETOOLONG;
|
"no iommu_group found");
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
group_path[len] = 0;
|
group_path[len] = 0;
|
||||||
|
|
||||||
group_name = basename(group_path);
|
group_name = basename(group_path);
|
||||||
if (sscanf(group_name, "%d", &groupid) != 1) {
|
if (sscanf(group_name, "%d", &groupid) != 1) {
|
||||||
error_report("vfio: error reading %s: %m", group_path);
|
error_setg_errno(errp, errno, "failed to read %s", group_path);
|
||||||
return -errno;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_vfio_initfn(vdev->vbasedev.name, groupid);
|
trace_vfio_realize(vdev->vbasedev.name, groupid);
|
||||||
|
|
||||||
group = vfio_get_group(groupid, pci_device_iommu_address_space(pdev));
|
group = vfio_get_group(groupid, pci_device_iommu_address_space(pdev), errp);
|
||||||
if (!group) {
|
if (!group) {
|
||||||
error_report("vfio: failed to get group %d", groupid);
|
goto error;
|
||||||
return -ENOENT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
|
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
|
||||||
if (strcmp(vbasedev_iter->name, vdev->vbasedev.name) == 0) {
|
if (strcmp(vbasedev_iter->name, vdev->vbasedev.name) == 0) {
|
||||||
error_report("vfio: error: device %s is already attached",
|
error_setg(errp, "device is already attached");
|
||||||
vdev->vbasedev.name);
|
|
||||||
vfio_put_group(group);
|
vfio_put_group(group);
|
||||||
return -EBUSY;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = vfio_get_device(group, vdev->vbasedev.name, &vdev->vbasedev);
|
ret = vfio_get_device(group, vdev->vbasedev.name, &vdev->vbasedev, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to get device %s", vdev->vbasedev.name);
|
|
||||||
vfio_put_group(group);
|
vfio_put_group(group);
|
||||||
return ret;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = vfio_populate_device(vdev);
|
vfio_populate_device(vdev, &err);
|
||||||
if (ret) {
|
if (err) {
|
||||||
return ret;
|
error_propagate(errp, err);
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get a copy of config space */
|
/* Get a copy of config space */
|
||||||
@ -2567,8 +2598,8 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
vdev->config_offset);
|
vdev->config_offset);
|
||||||
if (ret < (int)MIN(pci_config_size(&vdev->pdev), vdev->config_size)) {
|
if (ret < (int)MIN(pci_config_size(&vdev->pdev), vdev->config_size)) {
|
||||||
ret = ret < 0 ? -errno : -EFAULT;
|
ret = ret < 0 ? -errno : -EFAULT;
|
||||||
error_report("vfio: Failed to read device config space");
|
error_setg_errno(errp, -ret, "failed to read device config space");
|
||||||
return ret;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vfio emulates a lot for us, but some bits need extra love */
|
/* vfio emulates a lot for us, but some bits need extra love */
|
||||||
@ -2584,8 +2615,8 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
*/
|
*/
|
||||||
if (vdev->vendor_id != PCI_ANY_ID) {
|
if (vdev->vendor_id != PCI_ANY_ID) {
|
||||||
if (vdev->vendor_id >= 0xffff) {
|
if (vdev->vendor_id >= 0xffff) {
|
||||||
error_report("vfio: Invalid PCI vendor ID provided");
|
error_setg(errp, "invalid PCI vendor ID provided");
|
||||||
return -EINVAL;
|
goto error;
|
||||||
}
|
}
|
||||||
vfio_add_emulated_word(vdev, PCI_VENDOR_ID, vdev->vendor_id, ~0);
|
vfio_add_emulated_word(vdev, PCI_VENDOR_ID, vdev->vendor_id, ~0);
|
||||||
trace_vfio_pci_emulated_vendor_id(vdev->vbasedev.name, vdev->vendor_id);
|
trace_vfio_pci_emulated_vendor_id(vdev->vbasedev.name, vdev->vendor_id);
|
||||||
@ -2595,8 +2626,8 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
|
|
||||||
if (vdev->device_id != PCI_ANY_ID) {
|
if (vdev->device_id != PCI_ANY_ID) {
|
||||||
if (vdev->device_id > 0xffff) {
|
if (vdev->device_id > 0xffff) {
|
||||||
error_report("vfio: Invalid PCI device ID provided");
|
error_setg(errp, "invalid PCI device ID provided");
|
||||||
return -EINVAL;
|
goto error;
|
||||||
}
|
}
|
||||||
vfio_add_emulated_word(vdev, PCI_DEVICE_ID, vdev->device_id, ~0);
|
vfio_add_emulated_word(vdev, PCI_DEVICE_ID, vdev->device_id, ~0);
|
||||||
trace_vfio_pci_emulated_device_id(vdev->vbasedev.name, vdev->device_id);
|
trace_vfio_pci_emulated_device_id(vdev->vbasedev.name, vdev->device_id);
|
||||||
@ -2606,8 +2637,8 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
|
|
||||||
if (vdev->sub_vendor_id != PCI_ANY_ID) {
|
if (vdev->sub_vendor_id != PCI_ANY_ID) {
|
||||||
if (vdev->sub_vendor_id > 0xffff) {
|
if (vdev->sub_vendor_id > 0xffff) {
|
||||||
error_report("vfio: Invalid PCI subsystem vendor ID provided");
|
error_setg(errp, "invalid PCI subsystem vendor ID provided");
|
||||||
return -EINVAL;
|
goto error;
|
||||||
}
|
}
|
||||||
vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_VENDOR_ID,
|
vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_VENDOR_ID,
|
||||||
vdev->sub_vendor_id, ~0);
|
vdev->sub_vendor_id, ~0);
|
||||||
@ -2617,8 +2648,8 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
|
|
||||||
if (vdev->sub_device_id != PCI_ANY_ID) {
|
if (vdev->sub_device_id != PCI_ANY_ID) {
|
||||||
if (vdev->sub_device_id > 0xffff) {
|
if (vdev->sub_device_id > 0xffff) {
|
||||||
error_report("vfio: Invalid PCI subsystem device ID provided");
|
error_setg(errp, "invalid PCI subsystem device ID provided");
|
||||||
return -EINVAL;
|
goto error;
|
||||||
}
|
}
|
||||||
vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_ID, vdev->sub_device_id, ~0);
|
vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_ID, vdev->sub_device_id, ~0);
|
||||||
trace_vfio_pci_emulated_sub_device_id(vdev->vbasedev.name,
|
trace_vfio_pci_emulated_sub_device_id(vdev->vbasedev.name,
|
||||||
@ -2646,14 +2677,15 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
|
|
||||||
vfio_pci_size_rom(vdev);
|
vfio_pci_size_rom(vdev);
|
||||||
|
|
||||||
ret = vfio_msix_early_setup(vdev);
|
vfio_msix_early_setup(vdev, &err);
|
||||||
if (ret) {
|
if (err) {
|
||||||
return ret;
|
error_propagate(errp, err);
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
vfio_bars_setup(vdev);
|
vfio_bars_setup(vdev);
|
||||||
|
|
||||||
ret = vfio_add_capabilities(vdev);
|
ret = vfio_add_capabilities(vdev, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
goto out_teardown;
|
goto out_teardown;
|
||||||
}
|
}
|
||||||
@ -2671,9 +2703,9 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
struct vfio_region_info *opregion;
|
struct vfio_region_info *opregion;
|
||||||
|
|
||||||
if (vdev->pdev.qdev.hotplugged) {
|
if (vdev->pdev.qdev.hotplugged) {
|
||||||
error_report("Cannot support IGD OpRegion feature on hotplugged "
|
error_setg(errp,
|
||||||
"device %s", vdev->vbasedev.name);
|
"cannot support IGD OpRegion feature on hotplugged "
|
||||||
ret = -EINVAL;
|
"device");
|
||||||
goto out_teardown;
|
goto out_teardown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2681,16 +2713,14 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
|
VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
|
||||||
VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion);
|
VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("Device %s does not support requested IGD OpRegion "
|
error_setg_errno(errp, -ret,
|
||||||
"feature", vdev->vbasedev.name);
|
"does not support requested IGD OpRegion feature");
|
||||||
goto out_teardown;
|
goto out_teardown;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = vfio_pci_igd_opregion_init(vdev, opregion);
|
ret = vfio_pci_igd_opregion_init(vdev, opregion, errp);
|
||||||
g_free(opregion);
|
g_free(opregion);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("Device %s IGD OpRegion initialization failed",
|
|
||||||
vdev->vbasedev.name);
|
|
||||||
goto out_teardown;
|
goto out_teardown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2710,7 +2740,7 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
|
vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
|
||||||
vfio_intx_mmap_enable, vdev);
|
vfio_intx_mmap_enable, vdev);
|
||||||
pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_intx_update);
|
pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_intx_update);
|
||||||
ret = vfio_intx_enable(vdev);
|
ret = vfio_intx_enable(vdev, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
goto out_teardown;
|
goto out_teardown;
|
||||||
}
|
}
|
||||||
@ -2720,13 +2750,14 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
vfio_register_req_notifier(vdev);
|
vfio_register_req_notifier(vdev);
|
||||||
vfio_setup_resetfn_quirk(vdev);
|
vfio_setup_resetfn_quirk(vdev);
|
||||||
|
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
out_teardown:
|
out_teardown:
|
||||||
pci_device_set_intx_routing_notifier(&vdev->pdev, NULL);
|
pci_device_set_intx_routing_notifier(&vdev->pdev, NULL);
|
||||||
vfio_teardown_msi(vdev);
|
vfio_teardown_msi(vdev);
|
||||||
vfio_bars_exit(vdev);
|
vfio_bars_exit(vdev);
|
||||||
return ret;
|
error:
|
||||||
|
error_prepend(errp, ERR_PREFIX, vdev->vbasedev.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_instance_finalize(Object *obj)
|
static void vfio_instance_finalize(Object *obj)
|
||||||
@ -2808,6 +2839,10 @@ static void vfio_instance_init(Object *obj)
|
|||||||
device_add_bootindex_property(obj, &vdev->bootindex,
|
device_add_bootindex_property(obj, &vdev->bootindex,
|
||||||
"bootindex", NULL,
|
"bootindex", NULL,
|
||||||
&pci_dev->qdev, NULL);
|
&pci_dev->qdev, NULL);
|
||||||
|
vdev->host.domain = ~0U;
|
||||||
|
vdev->host.bus = ~0U;
|
||||||
|
vdev->host.slot = ~0U;
|
||||||
|
vdev->host.function = ~0U;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Property vfio_pci_dev_properties[] = {
|
static Property vfio_pci_dev_properties[] = {
|
||||||
@ -2855,7 +2890,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data)
|
|||||||
dc->vmsd = &vfio_pci_vmstate;
|
dc->vmsd = &vfio_pci_vmstate;
|
||||||
dc->desc = "VFIO-based PCI device assignment";
|
dc->desc = "VFIO-based PCI device assignment";
|
||||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||||
pdc->init = vfio_initfn;
|
pdc->realize = vfio_realize;
|
||||||
pdc->exit = vfio_exitfn;
|
pdc->exit = vfio_exitfn;
|
||||||
pdc->config_read = vfio_pci_read_config;
|
pdc->config_read = vfio_pci_read_config;
|
||||||
pdc->config_write = vfio_pci_write_config;
|
pdc->config_write = vfio_pci_write_config;
|
||||||
|
@ -161,9 +161,10 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr);
|
|||||||
void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr);
|
void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr);
|
||||||
void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
|
void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev);
|
||||||
|
|
||||||
int vfio_populate_vga(VFIOPCIDevice *vdev);
|
int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp);
|
||||||
|
|
||||||
int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
|
int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
|
||||||
struct vfio_region_info *info);
|
struct vfio_region_info *info,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
#endif /* HW_VFIO_VFIO_PCI_H */
|
#endif /* HW_VFIO_VFIO_PCI_H */
|
||||||
|
@ -44,9 +44,10 @@ static inline bool vfio_irq_is_automasked(VFIOINTp *intp)
|
|||||||
* and add it into the list of IRQs
|
* and add it into the list of IRQs
|
||||||
* @vbasedev: the VFIO device handle
|
* @vbasedev: the VFIO device handle
|
||||||
* @info: irq info struct retrieved from VFIO driver
|
* @info: irq info struct retrieved from VFIO driver
|
||||||
|
* @errp: error object
|
||||||
*/
|
*/
|
||||||
static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev,
|
static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev,
|
||||||
struct vfio_irq_info info)
|
struct vfio_irq_info info, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
VFIOPlatformDevice *vdev =
|
VFIOPlatformDevice *vdev =
|
||||||
@ -69,7 +70,8 @@ static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev,
|
|||||||
if (ret) {
|
if (ret) {
|
||||||
g_free(intp->interrupt);
|
g_free(intp->interrupt);
|
||||||
g_free(intp);
|
g_free(intp);
|
||||||
error_report("vfio: Error: trigger event_notifier_init failed ");
|
error_setg_errno(errp, -ret,
|
||||||
|
"failed to initialize trigger eventd notifier");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (vfio_irq_is_automasked(intp)) {
|
if (vfio_irq_is_automasked(intp)) {
|
||||||
@ -80,7 +82,8 @@ static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev,
|
|||||||
g_free(intp->interrupt);
|
g_free(intp->interrupt);
|
||||||
g_free(intp->unmask);
|
g_free(intp->unmask);
|
||||||
g_free(intp);
|
g_free(intp);
|
||||||
error_report("vfio: Error: resamplefd event_notifier_init failed");
|
error_setg_errno(errp, -ret,
|
||||||
|
"failed to initialize resample eventd notifier");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,9 +459,10 @@ static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev)
|
|||||||
* vfio_populate_device - Allocate and populate MMIO region
|
* vfio_populate_device - Allocate and populate MMIO region
|
||||||
* and IRQ structs according to driver returned information
|
* and IRQ structs according to driver returned information
|
||||||
* @vbasedev: the VFIO device handle
|
* @vbasedev: the VFIO device handle
|
||||||
|
* @errp: error object
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static int vfio_populate_device(VFIODevice *vbasedev)
|
static int vfio_populate_device(VFIODevice *vbasedev, Error **errp)
|
||||||
{
|
{
|
||||||
VFIOINTp *intp, *tmp;
|
VFIOINTp *intp, *tmp;
|
||||||
int i, ret = -1;
|
int i, ret = -1;
|
||||||
@ -466,7 +470,7 @@ static int vfio_populate_device(VFIODevice *vbasedev)
|
|||||||
container_of(vbasedev, VFIOPlatformDevice, vbasedev);
|
container_of(vbasedev, VFIOPlatformDevice, vbasedev);
|
||||||
|
|
||||||
if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
|
if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) {
|
||||||
error_report("vfio: Um, this isn't a platform device");
|
error_setg(errp, "this isn't a platform device");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,7 +484,7 @@ static int vfio_populate_device(VFIODevice *vbasedev)
|
|||||||
vdev->regions[i], i, name);
|
vdev->regions[i], i, name);
|
||||||
g_free(name);
|
g_free(name);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: Error getting region %d info: %m", i);
|
error_setg_errno(errp, -ret, "failed to get region %d info", i);
|
||||||
goto reg_error;
|
goto reg_error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -496,16 +500,15 @@ static int vfio_populate_device(VFIODevice *vbasedev)
|
|||||||
irq.index = i;
|
irq.index = i;
|
||||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq);
|
ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: error getting device %s irq info",
|
error_setg_errno(errp, -ret, "failed to get device irq info");
|
||||||
vbasedev->name);
|
|
||||||
goto irq_err;
|
goto irq_err;
|
||||||
} else {
|
} else {
|
||||||
trace_vfio_platform_populate_interrupts(irq.index,
|
trace_vfio_platform_populate_interrupts(irq.index,
|
||||||
irq.count,
|
irq.count,
|
||||||
irq.flags);
|
irq.flags);
|
||||||
intp = vfio_init_intp(vbasedev, irq);
|
intp = vfio_init_intp(vbasedev, irq, errp);
|
||||||
if (!intp) {
|
if (!intp) {
|
||||||
error_report("vfio: Error installing IRQ %d up", i);
|
ret = -1;
|
||||||
goto irq_err;
|
goto irq_err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -538,13 +541,14 @@ static VFIODeviceOps vfio_platform_ops = {
|
|||||||
/**
|
/**
|
||||||
* vfio_base_device_init - perform preliminary VFIO setup
|
* vfio_base_device_init - perform preliminary VFIO setup
|
||||||
* @vbasedev: the VFIO device handle
|
* @vbasedev: the VFIO device handle
|
||||||
|
* @errp: error object
|
||||||
*
|
*
|
||||||
* Implement the VFIO command sequence that allows to discover
|
* Implement the VFIO command sequence that allows to discover
|
||||||
* assigned device resources: group extraction, device
|
* assigned device resources: group extraction, device
|
||||||
* fd retrieval, resource query.
|
* fd retrieval, resource query.
|
||||||
* Precondition: the device name must be initialized
|
* Precondition: the device name must be initialized
|
||||||
*/
|
*/
|
||||||
static int vfio_base_device_init(VFIODevice *vbasedev)
|
static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp)
|
||||||
{
|
{
|
||||||
VFIOGroup *group;
|
VFIOGroup *group;
|
||||||
VFIODevice *vbasedev_iter;
|
VFIODevice *vbasedev_iter;
|
||||||
@ -560,6 +564,7 @@ static int vfio_base_device_init(VFIODevice *vbasedev)
|
|||||||
vbasedev->name = g_strdup(basename(vbasedev->sysfsdev));
|
vbasedev->name = g_strdup(basename(vbasedev->sysfsdev));
|
||||||
} else {
|
} else {
|
||||||
if (!vbasedev->name || strchr(vbasedev->name, '/')) {
|
if (!vbasedev->name || strchr(vbasedev->name, '/')) {
|
||||||
|
error_setg(errp, "wrong host device name");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,8 +573,8 @@ static int vfio_base_device_init(VFIODevice *vbasedev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stat(vbasedev->sysfsdev, &st) < 0) {
|
if (stat(vbasedev->sysfsdev, &st) < 0) {
|
||||||
error_report("vfio: error: no such host device: %s",
|
error_setg_errno(errp, errno,
|
||||||
vbasedev->sysfsdev);
|
"failed to get the sysfs host device file status");
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,44 +583,41 @@ static int vfio_base_device_init(VFIODevice *vbasedev)
|
|||||||
g_free(tmp);
|
g_free(tmp);
|
||||||
|
|
||||||
if (len < 0 || len >= sizeof(group_path)) {
|
if (len < 0 || len >= sizeof(group_path)) {
|
||||||
error_report("vfio: error no iommu_group for device");
|
ret = len < 0 ? -errno : -ENAMETOOLONG;
|
||||||
return len < 0 ? -errno : -ENAMETOOLONG;
|
error_setg_errno(errp, -ret, "no iommu_group found");
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
group_path[len] = 0;
|
group_path[len] = 0;
|
||||||
|
|
||||||
group_name = basename(group_path);
|
group_name = basename(group_path);
|
||||||
if (sscanf(group_name, "%d", &groupid) != 1) {
|
if (sscanf(group_name, "%d", &groupid) != 1) {
|
||||||
error_report("vfio: error reading %s: %m", group_path);
|
error_setg_errno(errp, errno, "failed to read %s", group_path);
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_vfio_platform_base_device_init(vbasedev->name, groupid);
|
trace_vfio_platform_base_device_init(vbasedev->name, groupid);
|
||||||
|
|
||||||
group = vfio_get_group(groupid, &address_space_memory);
|
group = vfio_get_group(groupid, &address_space_memory, errp);
|
||||||
if (!group) {
|
if (!group) {
|
||||||
error_report("vfio: failed to get group %d", groupid);
|
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
|
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
|
||||||
if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) {
|
if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) {
|
||||||
error_report("vfio: error: device %s is already attached",
|
error_setg(errp, "device is already attached");
|
||||||
vbasedev->name);
|
|
||||||
vfio_put_group(group);
|
vfio_put_group(group);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = vfio_get_device(group, vbasedev->name, vbasedev);
|
ret = vfio_get_device(group, vbasedev->name, vbasedev, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to get device %s", vbasedev->name);
|
|
||||||
vfio_put_group(group);
|
vfio_put_group(group);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = vfio_populate_device(vbasedev);
|
ret = vfio_populate_device(vbasedev, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to populate device %s", vbasedev->name);
|
|
||||||
vfio_put_group(group);
|
vfio_put_group(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,11 +646,9 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
|
|||||||
vbasedev->sysfsdev : vbasedev->name,
|
vbasedev->sysfsdev : vbasedev->name,
|
||||||
vdev->compat);
|
vdev->compat);
|
||||||
|
|
||||||
ret = vfio_base_device_init(vbasedev);
|
ret = vfio_base_device_init(vbasedev, errp);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_setg(errp, "vfio: vfio_base_device_init failed for %s",
|
goto out;
|
||||||
vbasedev->name);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < vbasedev->num_regions; i++) {
|
for (i = 0; i < vbasedev->num_regions; i++) {
|
||||||
@ -658,6 +658,16 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp)
|
|||||||
}
|
}
|
||||||
sysbus_init_mmio(sbdev, vdev->regions[i]->mem);
|
sysbus_init_mmio(sbdev, vdev->regions[i]->mem);
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
|
if (!ret) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev->vbasedev.name) {
|
||||||
|
error_prepend(errp, ERR_PREFIX, vdev->vbasedev.name);
|
||||||
|
} else {
|
||||||
|
error_prepend(errp, "vfio error: ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const VMStateDescription vfio_platform_vmstate = {
|
static const VMStateDescription vfio_platform_vmstate = {
|
||||||
|
@ -36,7 +36,7 @@ vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int
|
|||||||
vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s"
|
vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s"
|
||||||
vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx"
|
vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx"
|
||||||
vfio_populate_device_get_irq_info_failure(void) "VFIO_DEVICE_GET_IRQ_INFO failure: %m"
|
vfio_populate_device_get_irq_info_failure(void) "VFIO_DEVICE_GET_IRQ_INFO failure: %m"
|
||||||
vfio_initfn(const char *name, int group_id) " (%s) group %d"
|
vfio_realize(const char *name, int group_id) " (%s) group %d"
|
||||||
vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s %x@%x"
|
vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s %x@%x"
|
||||||
vfio_pci_reset(const char *name) " (%s)"
|
vfio_pci_reset(const char *name) " (%s)"
|
||||||
vfio_pci_reset_flr(const char *name) "%s FLR/VFIO_DEVICE_RESET"
|
vfio_pci_reset_flr(const char *name) "%s FLR/VFIO_DEVICE_RESET"
|
||||||
|
@ -30,6 +30,9 @@
|
|||||||
#include <linux/vfio.h>
|
#include <linux/vfio.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ERR_PREFIX "vfio error: %s: "
|
||||||
|
#define WARN_PREFIX "vfio warning: %s: "
|
||||||
|
|
||||||
/*#define DEBUG_VFIO*/
|
/*#define DEBUG_VFIO*/
|
||||||
#ifdef DEBUG_VFIO
|
#ifdef DEBUG_VFIO
|
||||||
#define DPRINTF(fmt, ...) \
|
#define DPRINTF(fmt, ...) \
|
||||||
@ -152,10 +155,10 @@ void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
|
|||||||
void vfio_region_exit(VFIORegion *region);
|
void vfio_region_exit(VFIORegion *region);
|
||||||
void vfio_region_finalize(VFIORegion *region);
|
void vfio_region_finalize(VFIORegion *region);
|
||||||
void vfio_reset_handler(void *opaque);
|
void vfio_reset_handler(void *opaque);
|
||||||
VFIOGroup *vfio_get_group(int groupid, AddressSpace *as);
|
VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp);
|
||||||
void vfio_put_group(VFIOGroup *group);
|
void vfio_put_group(VFIOGroup *group);
|
||||||
int vfio_get_device(VFIOGroup *group, const char *name,
|
int vfio_get_device(VFIOGroup *group, const char *name,
|
||||||
VFIODevice *vbasedev);
|
VFIODevice *vbasedev, Error **errp);
|
||||||
|
|
||||||
extern const MemoryRegionOps vfio_region_ops;
|
extern const MemoryRegionOps vfio_region_ops;
|
||||||
extern QLIST_HEAD(vfio_group_head, VFIOGroup) vfio_group_list;
|
extern QLIST_HEAD(vfio_group_head, VFIOGroup) vfio_group_list;
|
||||||
|
Loading…
Reference in New Issue
Block a user