Skip to content

Commit

Permalink
Merge tag 'vfio-v4.6-rc1' of git://github.com/awilliam/linux-vfio
Browse files Browse the repository at this point in the history
Pull VFIO updates from Alex Williamson:
 "Various enablers for assignment of Intel graphics devices and future
  support of vGPU devices (Alex Williamson).  This includes

   - Handling the vfio type1 interface as an API rather than a specific
     implementation, allowing multiple type1 providers.

   - Capability chains, similar to PCI device capabilities, that allow
     extending ioctls.  Extensions here include device specific regions
     and sparse mmap descriptions.  The former is used to expose non-PCI
     regions for IGD, including the OpRegion (particularly the Video
     BIOS Table), and read only PCI config access to the host and LPC
     bridge as drivers often depend on identifying those devices.

     Sparse mmaps here are used to describe the MSIx vector table, which
     vfio has always protected from mmap, but never had an API to
     explicitly define that protection.  In future vGPU support this is
     expected to allow the description of PCI BARs that may mix direct
     access and emulated access within a single region.

   - The ability to expose the shadow ROM as an option ROM as IGD use
     cases may rely on the ROM even though the physical device does not
     make use of a PCI option ROM BAR"

* tag 'vfio-v4.6-rc1' of git://github.com/awilliam/linux-vfio:
  vfio/pci: return -EFAULT if copy_to_user fails
  vfio/pci: Expose shadow ROM as PCI option ROM
  vfio/pci: Intel IGD host and LCP bridge config space access
  vfio/pci: Intel IGD OpRegion support
  vfio/pci: Enable virtual register in PCI config space
  vfio/pci: Add infrastructure for additional device specific regions
  vfio: Define device specific region type capability
  vfio/pci: Include sparse mmap capability for MSI-X table regions
  vfio: Define sparse mmap capability for regions
  vfio: Add capability chain helpers
  vfio: Define capability chains
  vfio: If an IOMMU backend fails, keep looking
  vfio/pci: Fix unsigned comparison overflow
  • Loading branch information
Linus Torvalds committed Mar 17, 2016
2 parents 3c0b8d1 + c4aec31 commit 45cb523
Show file tree
Hide file tree
Showing 11 changed files with 706 additions and 37 deletions.
4 changes: 4 additions & 0 deletions drivers/vfio/pci/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ config VFIO_PCI_MMAP
config VFIO_PCI_INTX
depends on VFIO_PCI
def_bool y if !S390

config VFIO_PCI_IGD
depends on VFIO_PCI
def_bool y if X86
1 change: 1 addition & 0 deletions drivers/vfio/pci/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
vfio-pci-$(CONFIG_VFIO_PCI_IGD) += vfio_pci_igd.o

obj-$(CONFIG_VFIO_PCI) += vfio-pci.o
175 changes: 168 additions & 7 deletions drivers/vfio/pci/vfio_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ static inline bool vfio_pci_is_vga(struct pci_dev *pdev)
}

static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev);
static void vfio_pci_disable(struct vfio_pci_device *vdev);

static int vfio_pci_enable(struct vfio_pci_device *vdev)
{
Expand Down Expand Up @@ -169,13 +170,26 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev)
if (!vfio_vga_disabled() && vfio_pci_is_vga(pdev))
vdev->has_vga = true;


if (vfio_pci_is_vga(pdev) &&
pdev->vendor == PCI_VENDOR_ID_INTEL &&
IS_ENABLED(CONFIG_VFIO_PCI_IGD)) {
ret = vfio_pci_igd_init(vdev);
if (ret) {
dev_warn(&vdev->pdev->dev,
"Failed to setup Intel IGD regions\n");
vfio_pci_disable(vdev);
return ret;
}
}

return 0;
}

static void vfio_pci_disable(struct vfio_pci_device *vdev)
{
struct pci_dev *pdev = vdev->pdev;
int bar;
int i, bar;

/* Stop the device from further DMA */
pci_clear_master(pdev);
Expand All @@ -186,6 +200,13 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev)

vdev->virq_disabled = false;

for (i = 0; i < vdev->num_regions; i++)
vdev->region[i].ops->release(vdev, &vdev->region[i]);

vdev->num_regions = 0;
kfree(vdev->region);
vdev->region = NULL; /* don't krealloc a freed pointer */

vfio_config_free(vdev);

for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) {
Expand Down Expand Up @@ -421,6 +442,93 @@ static int vfio_pci_for_each_slot_or_bus(struct pci_dev *pdev,
return walk.ret;
}

static int msix_sparse_mmap_cap(struct vfio_pci_device *vdev,
struct vfio_info_cap *caps)
{
struct vfio_info_cap_header *header;
struct vfio_region_info_cap_sparse_mmap *sparse;
size_t end, size;
int nr_areas = 2, i = 0;

end = pci_resource_len(vdev->pdev, vdev->msix_bar);

/* If MSI-X table is aligned to the start or end, only one area */
if (((vdev->msix_offset & PAGE_MASK) == 0) ||
(PAGE_ALIGN(vdev->msix_offset + vdev->msix_size) >= end))
nr_areas = 1;

size = sizeof(*sparse) + (nr_areas * sizeof(*sparse->areas));

header = vfio_info_cap_add(caps, size,
VFIO_REGION_INFO_CAP_SPARSE_MMAP, 1);
if (IS_ERR(header))
return PTR_ERR(header);

sparse = container_of(header,
struct vfio_region_info_cap_sparse_mmap, header);
sparse->nr_areas = nr_areas;

if (vdev->msix_offset & PAGE_MASK) {
sparse->areas[i].offset = 0;
sparse->areas[i].size = vdev->msix_offset & PAGE_MASK;
i++;
}

if (PAGE_ALIGN(vdev->msix_offset + vdev->msix_size) < end) {
sparse->areas[i].offset = PAGE_ALIGN(vdev->msix_offset +
vdev->msix_size);
sparse->areas[i].size = end - sparse->areas[i].offset;
i++;
}

return 0;
}

static int region_type_cap(struct vfio_pci_device *vdev,
struct vfio_info_cap *caps,
unsigned int type, unsigned int subtype)
{
struct vfio_info_cap_header *header;
struct vfio_region_info_cap_type *cap;

header = vfio_info_cap_add(caps, sizeof(*cap),
VFIO_REGION_INFO_CAP_TYPE, 1);
if (IS_ERR(header))
return PTR_ERR(header);

cap = container_of(header, struct vfio_region_info_cap_type, header);
cap->type = type;
cap->subtype = subtype;

return 0;
}

int vfio_pci_register_dev_region(struct vfio_pci_device *vdev,
unsigned int type, unsigned int subtype,
const struct vfio_pci_regops *ops,
size_t size, u32 flags, void *data)
{
struct vfio_pci_region *region;

region = krealloc(vdev->region,
(vdev->num_regions + 1) * sizeof(*region),
GFP_KERNEL);
if (!region)
return -ENOMEM;

vdev->region = region;
vdev->region[vdev->num_regions].type = type;
vdev->region[vdev->num_regions].subtype = subtype;
vdev->region[vdev->num_regions].ops = ops;
vdev->region[vdev->num_regions].size = size;
vdev->region[vdev->num_regions].flags = flags;
vdev->region[vdev->num_regions].data = data;

vdev->num_regions++;

return 0;
}

static long vfio_pci_ioctl(void *device_data,
unsigned int cmd, unsigned long arg)
{
Expand All @@ -443,7 +551,7 @@ static long vfio_pci_ioctl(void *device_data,
if (vdev->reset_works)
info.flags |= VFIO_DEVICE_FLAGS_RESET;

info.num_regions = VFIO_PCI_NUM_REGIONS;
info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions;
info.num_irqs = VFIO_PCI_NUM_IRQS;

return copy_to_user((void __user *)arg, &info, minsz) ?
Expand All @@ -452,6 +560,8 @@ static long vfio_pci_ioctl(void *device_data,
} else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
struct pci_dev *pdev = vdev->pdev;
struct vfio_region_info info;
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
int i, ret;

minsz = offsetofend(struct vfio_region_info, offset);

Expand Down Expand Up @@ -480,8 +590,15 @@ static long vfio_pci_ioctl(void *device_data,
VFIO_REGION_INFO_FLAG_WRITE;
if (IS_ENABLED(CONFIG_VFIO_PCI_MMAP) &&
pci_resource_flags(pdev, info.index) &
IORESOURCE_MEM && info.size >= PAGE_SIZE)
IORESOURCE_MEM && info.size >= PAGE_SIZE) {
info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
if (info.index == vdev->msix_bar) {
ret = msix_sparse_mmap_cap(vdev, &caps);
if (ret)
return ret;
}
}

break;
case VFIO_PCI_ROM_REGION_INDEX:
{
Expand All @@ -493,8 +610,14 @@ static long vfio_pci_ioctl(void *device_data,

/* Report the BAR size, not the ROM size */
info.size = pci_resource_len(pdev, info.index);
if (!info.size)
break;
if (!info.size) {
/* Shadow ROMs appear as PCI option ROMs */
if (pdev->resource[PCI_ROM_RESOURCE].flags &
IORESOURCE_ROM_SHADOW)
info.size = 0x20000;
else
break;
}

/* Is it really there? */
io = pci_map_rom(pdev, &size);
Expand All @@ -518,7 +641,40 @@ static long vfio_pci_ioctl(void *device_data,

break;
default:
return -EINVAL;
if (info.index >=
VFIO_PCI_NUM_REGIONS + vdev->num_regions)
return -EINVAL;

i = info.index - VFIO_PCI_NUM_REGIONS;

info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
info.size = vdev->region[i].size;
info.flags = vdev->region[i].flags;

ret = region_type_cap(vdev, &caps,
vdev->region[i].type,
vdev->region[i].subtype);
if (ret)
return ret;
}

if (caps.size) {
info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
if (info.argsz < sizeof(info) + caps.size) {
info.argsz = sizeof(info) + caps.size;
info.cap_offset = 0;
} else {
vfio_info_cap_shift(&caps, sizeof(info));
if (copy_to_user((void __user *)arg +
sizeof(info), caps.buf,
caps.size)) {
kfree(caps.buf);
return -EFAULT;
}
info.cap_offset = sizeof(info);
}

kfree(caps.buf);
}

return copy_to_user((void __user *)arg, &info, minsz) ?
Expand Down Expand Up @@ -798,7 +954,7 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,
unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
struct vfio_pci_device *vdev = device_data;

if (index >= VFIO_PCI_NUM_REGIONS)
if (index >= VFIO_PCI_NUM_REGIONS + vdev->num_regions)
return -EINVAL;

switch (index) {
Expand All @@ -815,6 +971,10 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf,

case VFIO_PCI_VGA_REGION_INDEX:
return vfio_pci_vga_rw(vdev, buf, count, ppos, iswrite);
default:
index -= VFIO_PCI_NUM_REGIONS;
return vdev->region[index].ops->rw(vdev, buf,
count, ppos, iswrite);
}

return -EINVAL;
Expand Down Expand Up @@ -997,6 +1157,7 @@ static void vfio_pci_remove(struct pci_dev *pdev)
return;

vfio_iommu_group_put(pdev->dev.iommu_group, &pdev->dev);
kfree(vdev->region);
kfree(vdev);

if (vfio_pci_is_vga(pdev)) {
Expand Down
45 changes: 38 additions & 7 deletions drivers/vfio/pci/vfio_pci_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@

#define PCI_CFG_SPACE_SIZE 256

/* Useful "pseudo" capabilities */
/* Fake capability ID for standard config space */
#define PCI_CAP_ID_BASIC 0
#define PCI_CAP_ID_INVALID 0xFF

#define is_bar(offset) \
((offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4) || \
Expand Down Expand Up @@ -301,6 +300,23 @@ static int vfio_raw_config_read(struct vfio_pci_device *vdev, int pos,
return count;
}

/* Virt access uses only virtualization */
static int vfio_virt_config_write(struct vfio_pci_device *vdev, int pos,
int count, struct perm_bits *perm,
int offset, __le32 val)
{
memcpy(vdev->vconfig + pos, &val, count);
return count;
}

static int vfio_virt_config_read(struct vfio_pci_device *vdev, int pos,
int count, struct perm_bits *perm,
int offset, __le32 *val)
{
memcpy(val, vdev->vconfig + pos, count);
return count;
}

/* Default capability regions to read-only, no-virtualization */
static struct perm_bits cap_perms[PCI_CAP_ID_MAX + 1] = {
[0 ... PCI_CAP_ID_MAX] = { .readfn = vfio_direct_config_read }
Expand All @@ -319,6 +335,11 @@ static struct perm_bits unassigned_perms = {
.writefn = vfio_raw_config_write
};

static struct perm_bits virt_perms = {
.readfn = vfio_virt_config_read,
.writefn = vfio_virt_config_write
};

static void free_perm_bits(struct perm_bits *perm)
{
kfree(perm->virt);
Expand Down Expand Up @@ -454,14 +475,19 @@ static void vfio_bar_fixup(struct vfio_pci_device *vdev)
bar = (__le32 *)&vdev->vconfig[PCI_ROM_ADDRESS];

/*
* NB. we expose the actual BAR size here, regardless of whether
* we can read it. When we report the REGION_INFO for the ROM
* we report what PCI tells us is the actual ROM size.
* NB. REGION_INFO will have reported zero size if we weren't able
* to read the ROM, but we still return the actual BAR size here if
* it exists (or the shadow ROM space).
*/
if (pci_resource_start(pdev, PCI_ROM_RESOURCE)) {
mask = ~(pci_resource_len(pdev, PCI_ROM_RESOURCE) - 1);
mask |= PCI_ROM_ADDRESS_ENABLE;
*bar &= cpu_to_le32((u32)mask);
} else if (pdev->resource[PCI_ROM_RESOURCE].flags &
IORESOURCE_ROM_SHADOW) {
mask = ~(0x20000 - 1);
mask |= PCI_ROM_ADDRESS_ENABLE;
*bar &= cpu_to_le32((u32)mask);
} else
*bar = 0;

Expand Down Expand Up @@ -1332,6 +1358,8 @@ static int vfio_cap_init(struct vfio_pci_device *vdev)
pos + i, map[pos + i], cap);
}

BUILD_BUG_ON(PCI_CAP_ID_MAX >= PCI_CAP_ID_INVALID_VIRT);

memset(map + pos, cap, len);
ret = vfio_fill_vconfig_bytes(vdev, pos, len);
if (ret)
Expand Down Expand Up @@ -1419,9 +1447,9 @@ static int vfio_ecap_init(struct vfio_pci_device *vdev)
/*
* Even though ecap is 2 bytes, we're currently a long way
* from exceeding 1 byte capabilities. If we ever make it
* up to 0xFF we'll need to up this to a two-byte, byte map.
* up to 0xFE we'll need to up this to a two-byte, byte map.
*/
BUILD_BUG_ON(PCI_EXT_CAP_ID_MAX >= PCI_CAP_ID_INVALID);
BUILD_BUG_ON(PCI_EXT_CAP_ID_MAX >= PCI_CAP_ID_INVALID_VIRT);

memset(map + epos, ecap, len);
ret = vfio_fill_vconfig_bytes(vdev, epos, len);
Expand Down Expand Up @@ -1597,6 +1625,9 @@ static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf,
if (cap_id == PCI_CAP_ID_INVALID) {
perm = &unassigned_perms;
cap_start = *ppos;
} else if (cap_id == PCI_CAP_ID_INVALID_VIRT) {
perm = &virt_perms;
cap_start = *ppos;
} else {
if (*ppos >= PCI_CFG_SPACE_SIZE) {
WARN_ON(cap_id > PCI_EXT_CAP_ID_MAX);
Expand Down
Loading

0 comments on commit 45cb523

Please sign in to comment.