VFIO for 3.9-rc1

- Fixes PCIe v1 extended capability support
 - Cleans up read/write access functions
 - Fix Removal test to properly wait until devices are unused
 - Enable pcieport driver usage for non-accessible devices w/in groups
 - Extensions for PCI VGA support
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.13 (GNU/Linux)
 
 iQIcBAABAgAGBQJRKkm1AAoJECObm247sIsiFIsQAIcppX9JOJ9pTIxx59G8CN0V
 LXJbTMPn3PtuiiMvS/+XqVTrdHDayddjjnaIHVVnYoTe2bPreCPdG8ViN37XjGLS
 JbxfGsslR7ZUH7PuO8fQIj64tA7sDKsquqchy+RmjNJujygDd6HSCw/K4eRkCX3z
 nZmDIlBcnqPLQi5zmwirdfzR+J0nKWWKvvqNOGgNTYfWj8M1WGH2M4Fi0ohLtVR8
 1wJpZjwVPpwgMgMOMLBlFurQoKlibOKghjoOeJoMsBZ2gBi4iPustOJc+bwbF9SQ
 IuYpU3qGBUKWF6lvTlTMkgvGKBSqNgJBcTSJo0IvQounX1tIVYvRa2Bc1pQ4c+5p
 rFaD0+MCvRiyCSY85ZqvK7V6hfY8zTrhDm0eqCcW2hyy5xFJOh9jRxdmvAHSIA/A
 PWSy3wuDQJu6uIY+QNvWlo/iF2I8gh6b46Deqer3OuSCl3O2mjUjiSSqqR3BnO9p
 EG7mFMbvnv9hj0QxS53eYSUk0VRfu1EgfTLclbvo33JsCoVSk1Fb5zxHo04KRLZa
 da2pVM/B1NWtfscfQSNKLUpQ2UEMjfP48NXdbQriNoTZJIFQ6Qptg1NeRHPDK11x
 0nI7sPv6Qe09YNA7EO91PbphYbl7bE+tjeBFkpmWfY+gKZ8YqgilL/SfTUBkv/dU
 Ntb4IdOODxyBOLLKIFBd
 =S7w8
 -----END PGP SIGNATURE-----

Merge tag 'vfio-v3.9-rc1' of git://github.com/awilliam/linux-vfio

Pull VFIO updates from Alex Williamson:
 - Fixes PCIe v1 extended capability support

 - Cleans up read/write access functions

 - Fix Removal test to properly wait until devices are unused

 - Enable pcieport driver usage for non-accessible devices w/in groups

 - Extensions for PCI VGA support

* tag 'vfio-v3.9-rc1' of git://github.com/awilliam/linux-vfio:
  drivers/vfio: remove depends on CONFIG_EXPERIMENTAL
  vfio-pci: Add support for VGA region access
  vfio-pci: Manage user power state transitions
  vfio: whitelist pcieport
  vfio: Protect vfio_dev_present against device_del
  vfio-pci: Cleanup BAR access
  vfio-pci: Cleanup read/write functions
  vfio-pci: Enable PCIe extended capabilities on v1
This commit is contained in:
Linus Torvalds 2013-02-26 14:48:30 -08:00
commit 515d01f772
7 changed files with 284 additions and 257 deletions

View File

@ -6,3 +6,13 @@ config VFIO_PCI
use of PCI drivers using the VFIO framework. use of PCI drivers using the VFIO framework.
If you don't know what to do here, say N. If you don't know what to do here, say N.
config VFIO_PCI_VGA
bool "VFIO PCI support for VGA devices"
depends on VFIO_PCI && X86 && VGA_ARB
help
Support for VGA extension to VFIO PCI. This exposes an additional
region on VGA devices for accessing legacy VGA addresses used by
BIOS and generic video drivers.
If you don't know what to do here, say N.

View File

@ -84,6 +84,11 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
} else } else
vdev->msix_bar = 0xFF; vdev->msix_bar = 0xFF;
#ifdef CONFIG_VFIO_PCI_VGA
if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
vdev->has_vga = true;
#endif
return 0; return 0;
} }
@ -285,6 +290,16 @@ static long vfio_pci_ioctl(void *device_data,
info.flags = VFIO_REGION_INFO_FLAG_READ; info.flags = VFIO_REGION_INFO_FLAG_READ;
break; break;
} }
case VFIO_PCI_VGA_REGION_INDEX:
if (!vdev->has_vga)
return -EINVAL;
info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
info.size = 0xc0000;
info.flags = VFIO_REGION_INFO_FLAG_READ |
VFIO_REGION_INFO_FLAG_WRITE;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
@ -366,52 +381,50 @@ static long vfio_pci_ioctl(void *device_data,
return -ENOTTY; return -ENOTTY;
} }
static ssize_t vfio_pci_read(void *device_data, char __user *buf, static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos, bool iswrite)
{ {
unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
struct vfio_pci_device *vdev = device_data; struct vfio_pci_device *vdev = device_data;
struct pci_dev *pdev = vdev->pdev;
if (index >= VFIO_PCI_NUM_REGIONS) if (index >= VFIO_PCI_NUM_REGIONS)
return -EINVAL; return -EINVAL;
if (index == VFIO_PCI_CONFIG_REGION_INDEX) switch (index) {
return vfio_pci_config_readwrite(vdev, buf, count, ppos, false); case VFIO_PCI_CONFIG_REGION_INDEX:
else if (index == VFIO_PCI_ROM_REGION_INDEX) return vfio_pci_config_rw(vdev, buf, count, ppos, iswrite);
return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) case VFIO_PCI_ROM_REGION_INDEX:
return vfio_pci_io_readwrite(vdev, buf, count, ppos, false); if (iswrite)
else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) return -EINVAL;
return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); return vfio_pci_bar_rw(vdev, buf, count, ppos, false);
case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite);
case VFIO_PCI_VGA_REGION_INDEX:
return vfio_pci_vga_rw(vdev, buf, count, ppos, iswrite);
}
return -EINVAL; return -EINVAL;
} }
static ssize_t vfio_pci_read(void *device_data, char __user *buf,
size_t count, loff_t *ppos)
{
if (!count)
return 0;
return vfio_pci_rw(device_data, buf, count, ppos, false);
}
static ssize_t vfio_pci_write(void *device_data, const char __user *buf, static ssize_t vfio_pci_write(void *device_data, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); if (!count)
struct vfio_pci_device *vdev = device_data; return 0;
struct pci_dev *pdev = vdev->pdev;
if (index >= VFIO_PCI_NUM_REGIONS) return vfio_pci_rw(device_data, (char __user *)buf, count, ppos, true);
return -EINVAL;
if (index == VFIO_PCI_CONFIG_REGION_INDEX)
return vfio_pci_config_readwrite(vdev, (char __user *)buf,
count, ppos, true);
else if (index == VFIO_PCI_ROM_REGION_INDEX)
return -EINVAL;
else if (pci_resource_flags(pdev, index) & IORESOURCE_IO)
return vfio_pci_io_readwrite(vdev, (char __user *)buf,
count, ppos, true);
else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) {
return vfio_pci_mem_readwrite(vdev, (char __user *)buf,
count, ppos, true);
}
return -EINVAL;
} }
static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)

View File

@ -587,12 +587,46 @@ static int __init init_pci_cap_basic_perm(struct perm_bits *perm)
return 0; return 0;
} }
static int vfio_pm_config_write(struct vfio_pci_device *vdev, int pos,
int count, struct perm_bits *perm,
int offset, __le32 val)
{
count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
if (count < 0)
return count;
if (offset == PCI_PM_CTRL) {
pci_power_t state;
switch (le32_to_cpu(val) & PCI_PM_CTRL_STATE_MASK) {
case 0:
state = PCI_D0;
break;
case 1:
state = PCI_D1;
break;
case 2:
state = PCI_D2;
break;
case 3:
state = PCI_D3hot;
break;
}
pci_set_power_state(vdev->pdev, state);
}
return count;
}
/* Permissions for the Power Management capability */ /* Permissions for the Power Management capability */
static int __init init_pci_cap_pm_perm(struct perm_bits *perm) static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
{ {
if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM])) if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM]))
return -ENOMEM; return -ENOMEM;
perm->writefn = vfio_pm_config_write;
/* /*
* We always virtualize the next field so we can remove * We always virtualize the next field so we can remove
* capabilities from the chain if we want to. * capabilities from the chain if we want to.
@ -600,10 +634,11 @@ static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
/* /*
* Power management is defined *per function*, * Power management is defined *per function*, so we can let
* so we let the user write this * the user change power state, but we trap and initiate the
* change ourselves, so the state bits are read-only.
*/ */
p_setd(perm, PCI_PM_CTRL, NO_VIRT, ALL_WRITE); p_setd(perm, PCI_PM_CTRL, NO_VIRT, ~PCI_PM_CTRL_STATE_MASK);
return 0; return 0;
} }
@ -985,12 +1020,12 @@ static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos)
if (ret) if (ret)
return pcibios_err_to_errno(ret); return pcibios_err_to_errno(ret);
vdev->extended_caps = true;
if ((word & PCI_EXP_FLAGS_VERS) == 1) if ((word & PCI_EXP_FLAGS_VERS) == 1)
return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1; return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1;
else { else
vdev->extended_caps = true;
return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2; return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2;
}
case PCI_CAP_ID_HT: case PCI_CAP_ID_HT:
ret = pci_read_config_byte(pdev, pos + 3, &byte); ret = pci_read_config_byte(pdev, pos + 3, &byte);
if (ret) if (ret)
@ -1501,9 +1536,8 @@ static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf,
return ret; return ret;
} }
ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, char __user *buf,
char __user *buf, size_t count, size_t count, loff_t *ppos, bool iswrite)
loff_t *ppos, bool iswrite)
{ {
size_t done = 0; size_t done = 0;
int ret = 0; int ret = 0;

View File

@ -53,6 +53,7 @@ struct vfio_pci_device {
bool reset_works; bool reset_works;
bool extended_caps; bool extended_caps;
bool bardirty; bool bardirty;
bool has_vga;
struct pci_saved_state *pci_saved_state; struct pci_saved_state *pci_saved_state;
atomic_t refcnt; atomic_t refcnt;
}; };
@ -70,16 +71,16 @@ extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev,
uint32_t flags, unsigned index, uint32_t flags, unsigned index,
unsigned start, unsigned count, void *data); unsigned start, unsigned count, void *data);
extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev,
char __user *buf, size_t count,
loff_t *ppos, bool iswrite);
extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev,
char __user *buf, size_t count,
loff_t *ppos, bool iswrite);
extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev,
char __user *buf, size_t count, char __user *buf, size_t count,
loff_t *ppos, bool iswrite); loff_t *ppos, bool iswrite);
extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite);
extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite);
extern int vfio_pci_init_perm_bits(void); extern int vfio_pci_init_perm_bits(void);
extern void vfio_pci_uninit_perm_bits(void); extern void vfio_pci_uninit_perm_bits(void);

View File

@ -17,208 +17,61 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/vgaarb.h>
#include "vfio_pci_private.h" #include "vfio_pci_private.h"
/* I/O Port BAR access */
ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite)
{
struct pci_dev *pdev = vdev->pdev;
loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
void __iomem *io;
size_t done = 0;
if (!pci_resource_start(pdev, bar))
return -EINVAL;
if (pos + count > pci_resource_len(pdev, bar))
return -EINVAL;
if (!vdev->barmap[bar]) {
int ret;
ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
if (ret)
return ret;
vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
if (!vdev->barmap[bar]) {
pci_release_selected_regions(pdev, 1 << bar);
return -EINVAL;
}
}
io = vdev->barmap[bar];
while (count) {
int filled;
if (count >= 3 && !(pos % 4)) {
__le32 val;
if (iswrite) {
if (copy_from_user(&val, buf, 4))
return -EFAULT;
iowrite32(le32_to_cpu(val), io + pos);
} else {
val = cpu_to_le32(ioread32(io + pos));
if (copy_to_user(buf, &val, 4))
return -EFAULT;
}
filled = 4;
} else if ((pos % 2) == 0 && count >= 2) {
__le16 val;
if (iswrite) {
if (copy_from_user(&val, buf, 2))
return -EFAULT;
iowrite16(le16_to_cpu(val), io + pos);
} else {
val = cpu_to_le16(ioread16(io + pos));
if (copy_to_user(buf, &val, 2))
return -EFAULT;
}
filled = 2;
} else {
u8 val;
if (iswrite) {
if (copy_from_user(&val, buf, 1))
return -EFAULT;
iowrite8(val, io + pos);
} else {
val = ioread8(io + pos);
if (copy_to_user(buf, &val, 1))
return -EFAULT;
}
filled = 1;
}
count -= filled;
done += filled;
buf += filled;
pos += filled;
}
*ppos += done;
return done;
}
/* /*
* MMIO BAR access * Read or write from an __iomem region (MMIO or I/O port) with an excluded
* We handle two excluded ranges here as well, if the user tries to read * range which is inaccessible. The excluded range drops writes and fills
* the ROM beyond what PCI tells us is available or the MSI-X table region, * reads with -1. This is intended for handling MSI-X vector tables and
* we return 0xFF and writes are dropped. * leftover space for ROM BARs.
*/ */
ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, static ssize_t do_io_rw(void __iomem *io, char __user *buf,
size_t count, loff_t *ppos, bool iswrite) loff_t off, size_t count, size_t x_start,
size_t x_end, bool iswrite)
{ {
struct pci_dev *pdev = vdev->pdev; ssize_t done = 0;
loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
void __iomem *io;
resource_size_t end;
size_t done = 0;
size_t x_start = 0, x_end = 0; /* excluded range */
if (!pci_resource_start(pdev, bar))
return -EINVAL;
end = pci_resource_len(pdev, bar);
if (pos > end)
return -EINVAL;
if (pos == end)
return 0;
if (pos + count > end)
count = end - pos;
if (bar == PCI_ROM_RESOURCE) {
io = pci_map_rom(pdev, &x_start);
x_end = end;
} else {
if (!vdev->barmap[bar]) {
int ret;
ret = pci_request_selected_regions(pdev, 1 << bar,
"vfio");
if (ret)
return ret;
vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
if (!vdev->barmap[bar]) {
pci_release_selected_regions(pdev, 1 << bar);
return -EINVAL;
}
}
io = vdev->barmap[bar];
if (bar == vdev->msix_bar) {
x_start = vdev->msix_offset;
x_end = vdev->msix_offset + vdev->msix_size;
}
}
if (!io)
return -EINVAL;
while (count) { while (count) {
size_t fillable, filled; size_t fillable, filled;
if (pos < x_start) if (off < x_start)
fillable = x_start - pos; fillable = min(count, (size_t)(x_start - off));
else if (pos >= x_end) else if (off >= x_end)
fillable = end - pos; fillable = count;
else else
fillable = 0; fillable = 0;
if (fillable >= 4 && !(pos % 4) && (count >= 4)) { if (fillable >= 4 && !(off % 4)) {
__le32 val; __le32 val;
if (iswrite) { if (iswrite) {
if (copy_from_user(&val, buf, 4)) if (copy_from_user(&val, buf, 4))
goto out; return -EFAULT;
iowrite32(le32_to_cpu(val), io + pos); iowrite32(le32_to_cpu(val), io + off);
} else { } else {
val = cpu_to_le32(ioread32(io + pos)); val = cpu_to_le32(ioread32(io + off));
if (copy_to_user(buf, &val, 4)) if (copy_to_user(buf, &val, 4))
goto out; return -EFAULT;
} }
filled = 4; filled = 4;
} else if (fillable >= 2 && !(pos % 2) && (count >= 2)) { } else if (fillable >= 2 && !(off % 2)) {
__le16 val; __le16 val;
if (iswrite) { if (iswrite) {
if (copy_from_user(&val, buf, 2)) if (copy_from_user(&val, buf, 2))
goto out; return -EFAULT;
iowrite16(le16_to_cpu(val), io + pos); iowrite16(le16_to_cpu(val), io + off);
} else { } else {
val = cpu_to_le16(ioread16(io + pos)); val = cpu_to_le16(ioread16(io + off));
if (copy_to_user(buf, &val, 2)) if (copy_to_user(buf, &val, 2))
goto out; return -EFAULT;
} }
filled = 2; filled = 2;
@ -227,43 +80,159 @@ ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf,
if (iswrite) { if (iswrite) {
if (copy_from_user(&val, buf, 1)) if (copy_from_user(&val, buf, 1))
goto out; return -EFAULT;
iowrite8(val, io + pos); iowrite8(val, io + off);
} else { } else {
val = ioread8(io + pos); val = ioread8(io + off);
if (copy_to_user(buf, &val, 1)) if (copy_to_user(buf, &val, 1))
goto out; return -EFAULT;
} }
filled = 1; filled = 1;
} else { } else {
/* Drop writes, fill reads with FF */ /* Fill reads with -1, drop writes */
filled = min((size_t)(x_end - pos), count); filled = min(count, (size_t)(x_end - off));
if (!iswrite) { if (!iswrite) {
char val = 0xFF; u8 val = 0xFF;
size_t i; size_t i;
for (i = 0; i < filled; i++) { for (i = 0; i < filled; i++)
if (put_user(val, buf + i)) if (copy_to_user(buf + i, &val, 1))
goto out; return -EFAULT;
} }
} }
}
count -= filled; count -= filled;
done += filled; done += filled;
off += filled;
buf += filled; buf += filled;
pos += filled;
} }
return done;
}
ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite)
{
struct pci_dev *pdev = vdev->pdev;
loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
size_t x_start = 0, x_end = 0;
resource_size_t end;
void __iomem *io;
ssize_t done;
if (!pci_resource_start(pdev, bar))
return -EINVAL;
end = pci_resource_len(pdev, bar);
if (pos >= end)
return -EINVAL;
count = min(count, (size_t)(end - pos));
if (bar == PCI_ROM_RESOURCE) {
/*
* The ROM can fill less space than the BAR, so we start the
* excluded range at the end of the actual ROM. This makes
* filling large ROM BARs much faster.
*/
io = pci_map_rom(pdev, &x_start);
if (!io)
return -ENOMEM;
x_end = end;
} else if (!vdev->barmap[bar]) {
int ret;
ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
if (ret)
return ret;
io = pci_iomap(pdev, bar, 0);
if (!io) {
pci_release_selected_regions(pdev, 1 << bar);
return -ENOMEM;
}
vdev->barmap[bar] = io;
} else
io = vdev->barmap[bar];
if (bar == vdev->msix_bar) {
x_start = vdev->msix_offset;
x_end = vdev->msix_offset + vdev->msix_size;
}
done = do_io_rw(io, buf, pos, count, x_start, x_end, iswrite);
if (done >= 0)
*ppos += done; *ppos += done;
out:
if (bar == PCI_ROM_RESOURCE) if (bar == PCI_ROM_RESOURCE)
pci_unmap_rom(pdev, io); pci_unmap_rom(pdev, io);
return count ? -EFAULT : done; return done;
}
ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
size_t count, loff_t *ppos, bool iswrite)
{
int ret;
loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK;
void __iomem *iomem = NULL;
unsigned int rsrc;
bool is_ioport;
ssize_t done;
if (!vdev->has_vga)
return -EINVAL;
switch (pos) {
case 0xa0000 ... 0xbffff:
count = min(count, (size_t)(0xc0000 - pos));
iomem = ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1);
off = pos - 0xa0000;
rsrc = VGA_RSRC_LEGACY_MEM;
is_ioport = false;
break;
case 0x3b0 ... 0x3bb:
count = min(count, (size_t)(0x3bc - pos));
iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1);
off = pos - 0x3b0;
rsrc = VGA_RSRC_LEGACY_IO;
is_ioport = true;
break;
case 0x3c0 ... 0x3df:
count = min(count, (size_t)(0x3e0 - pos));
iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1);
off = pos - 0x3c0;
rsrc = VGA_RSRC_LEGACY_IO;
is_ioport = true;
break;
default:
return -EINVAL;
}
if (!iomem)
return -ENOMEM;
ret = vga_get_interruptible(vdev->pdev, rsrc);
if (ret) {
is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
return ret;
}
done = do_io_rw(iomem, buf, off, count, 0, 0, iswrite);
vga_put(vdev->pdev, rsrc);
is_ioport ? ioport_unmap(iomem) : iounmap(iomem);
if (done >= 0)
*ppos += done;
return done;
} }

View File

@ -442,7 +442,7 @@ static struct vfio_device *vfio_group_get_device(struct vfio_group *group,
* a device. It's not always practical to leave a device within a group * a device. It's not always practical to leave a device within a group
* driverless as it could get re-bound to something unsafe. * driverless as it could get re-bound to something unsafe.
*/ */
static const char * const vfio_driver_whitelist[] = { "pci-stub" }; static const char * const vfio_driver_whitelist[] = { "pci-stub", "pcieport" };
static bool vfio_whitelisted_driver(struct device_driver *drv) static bool vfio_whitelisted_driver(struct device_driver *drv)
{ {
@ -642,33 +642,16 @@ int vfio_add_group_dev(struct device *dev,
} }
EXPORT_SYMBOL_GPL(vfio_add_group_dev); EXPORT_SYMBOL_GPL(vfio_add_group_dev);
/* Test whether a struct device is present in our tracking */ /* Given a referenced group, check if it contains the device */
static bool vfio_dev_present(struct device *dev) static bool vfio_dev_present(struct vfio_group *group, struct device *dev)
{ {
struct iommu_group *iommu_group;
struct vfio_group *group;
struct vfio_device *device; struct vfio_device *device;
iommu_group = iommu_group_get(dev);
if (!iommu_group)
return false;
group = vfio_group_get_from_iommu(iommu_group);
if (!group) {
iommu_group_put(iommu_group);
return false;
}
device = vfio_group_get_device(group, dev); device = vfio_group_get_device(group, dev);
if (!device) { if (!device)
vfio_group_put(group);
iommu_group_put(iommu_group);
return false; return false;
}
vfio_device_put(device); vfio_device_put(device);
vfio_group_put(group);
iommu_group_put(iommu_group);
return true; return true;
} }
@ -682,10 +665,18 @@ void *vfio_del_group_dev(struct device *dev)
struct iommu_group *iommu_group = group->iommu_group; struct iommu_group *iommu_group = group->iommu_group;
void *device_data = device->device_data; void *device_data = device->device_data;
/*
* The group exists so long as we have a device reference. Get
* a group reference and use it to scan for the device going away.
*/
vfio_group_get(group);
vfio_device_put(device); vfio_device_put(device);
/* TODO send a signal to encourage this to be released */ /* TODO send a signal to encourage this to be released */
wait_event(vfio.release_q, !vfio_dev_present(dev)); wait_event(vfio.release_q, !vfio_dev_present(group, dev));
vfio_group_put(group);
iommu_group_put(iommu_group); iommu_group_put(iommu_group);

View File

@ -303,6 +303,15 @@ enum {
VFIO_PCI_BAR5_REGION_INDEX, VFIO_PCI_BAR5_REGION_INDEX,
VFIO_PCI_ROM_REGION_INDEX, VFIO_PCI_ROM_REGION_INDEX,
VFIO_PCI_CONFIG_REGION_INDEX, VFIO_PCI_CONFIG_REGION_INDEX,
/*
* Expose VGA regions defined for PCI base class 03, subclass 00.
* This includes I/O port ranges 0x3b0 to 0x3bb and 0x3c0 to 0x3df
* as well as the MMIO range 0xa0000 to 0xbffff. Each implemented
* range is found at it's identity mapped offset from the region
* offset, for example 0x3b0 is region_info.offset + 0x3b0. Areas
* between described ranges are unimplemented.
*/
VFIO_PCI_VGA_REGION_INDEX,
VFIO_PCI_NUM_REGIONS VFIO_PCI_NUM_REGIONS
}; };