Commit a40a1d3d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'vfio-for-v3.6' of git://github.com/awilliam/linux-vfio

Pull VFIO core from Alex Williamson:
 "This series includes the VFIO userspace driver interface for the 3.6
  kernel merge window.  This driver is intended to provide a secure
  interface for device access using IOMMU protection for applications
  like assignment of physical devices to virtual machines.

  Qemu will be the first user of this interface, enabling assignment of
  PCI devices to Qemu guests.  This interface is intended to eventually
  replace the x86-specific assignment mechanism currently available in
  KVM.

  This interface has the advantage of being more secure, by working with
  IOMMU groups to ensure device isolation and providing it's own
  filtered resource access mechanism, and also more flexible, in not
  being x86 or KVM specific (extensions to enable POWER are already
  working).

  This driver is originally the work of Tom Lyon, but has since been
  handed over to me and gone through a complete overhaul thanks to the
  input from David Gibson, Ben Herrenschmidt, Chris Wright, Joerg
  Roedel, and others.  This driver has been available in linux-next for
  the last month."

Paul Mackerras says:
 "I would be glad to see it go in since we want to use it with KVM on
  PowerPC.  If possible we'd like the PowerPC bits for it to go in as
  well."

* tag 'vfio-for-v3.6' of git://github.com/awilliam/linux-vfio:
  vfio: Add PCI device driver
  vfio: Type1 IOMMU implementation
  vfio: Add documentation
  vfio: VFIO core
parents 3e9a9708 89e1f7d4
......@@ -88,6 +88,7 @@ Code Seq#(hex) Include File Comments
and kernel/power/user.c
'8' all SNP8023 advanced NIC card
<mailto:mcr@solidum.com>
';' 64-7F linux/vfio.h
'@' 00-0F linux/radeonfb.h conflict!
'@' 00-0F drivers/video/aty/aty128fb.c conflict!
'A' 00-1F linux/apm_bios.h conflict!
......
This diff is collapsed.
......@@ -7414,6 +7414,14 @@ S: Maintained
F: Documentation/filesystems/vfat.txt
F: fs/fat/
VFIO DRIVER
M: Alex Williamson <alex.williamson@redhat.com>
L: kvm@vger.kernel.org
S: Maintained
F: Documentation/vfio.txt
F: drivers/vfio/
F: include/linux/vfio.h
VIDEOBUF2 FRAMEWORK
M: Pawel Osciak <pawel@osciak.com>
M: Marek Szyprowski <m.szyprowski@samsung.com>
......
......@@ -112,6 +112,8 @@ source "drivers/auxdisplay/Kconfig"
source "drivers/uio/Kconfig"
source "drivers/vfio/Kconfig"
source "drivers/vlynq/Kconfig"
source "drivers/virtio/Kconfig"
......
......@@ -60,6 +60,7 @@ obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
obj-y += firewire/
obj-$(CONFIG_UIO) += uio/
obj-$(CONFIG_VFIO) += vfio/
obj-y += cdrom/
obj-y += auxdisplay/
obj-$(CONFIG_PCCARD) += pcmcia/
......
config VFIO_IOMMU_TYPE1
tristate
depends on VFIO
default n
menuconfig VFIO
tristate "VFIO Non-Privileged userspace driver framework"
depends on IOMMU_API
select VFIO_IOMMU_TYPE1 if X86
help
VFIO provides a framework for secure userspace device drivers.
See Documentation/vfio.txt for more details.
If you don't know what to do here, say N.
source "drivers/vfio/pci/Kconfig"
obj-$(CONFIG_VFIO) += vfio.o
obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
obj-$(CONFIG_VFIO_PCI) += pci/
config VFIO_PCI
tristate "VFIO support for PCI devices"
depends on VFIO && PCI && EVENTFD
help
Support for the PCI VFIO bus driver. This is required to make
use of PCI drivers using the VFIO framework.
If you don't know what to do here, say N.
vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
obj-$(CONFIG_VFIO_PCI) += vfio-pci.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (C) 2012 Red Hat, Inc. All rights reserved.
* Author: Alex Williamson <alex.williamson@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Derived from original vfio:
* Copyright 2010 Cisco Systems, Inc. All rights reserved.
* Author: Tom Lyon, pugs@cisco.com
*/
#include <linux/mutex.h>
#include <linux/pci.h>
#ifndef VFIO_PCI_PRIVATE_H
#define VFIO_PCI_PRIVATE_H
#define VFIO_PCI_OFFSET_SHIFT 40
#define VFIO_PCI_OFFSET_TO_INDEX(off) (off >> VFIO_PCI_OFFSET_SHIFT)
#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
struct vfio_pci_irq_ctx {
struct eventfd_ctx *trigger;
struct virqfd *unmask;
struct virqfd *mask;
char *name;
bool masked;
};
struct vfio_pci_device {
struct pci_dev *pdev;
void __iomem *barmap[PCI_STD_RESOURCE_END + 1];
u8 *pci_config_map;
u8 *vconfig;
struct perm_bits *msi_perm;
spinlock_t irqlock;
struct mutex igate;
struct msix_entry *msix;
struct vfio_pci_irq_ctx *ctx;
int num_ctx;
int irq_type;
u8 msi_qmax;
u8 msix_bar;
u16 msix_size;
u32 msix_offset;
u32 rbar[7];
bool pci_2_3;
bool virq_disabled;
bool reset_works;
bool extended_caps;
bool bardirty;
struct pci_saved_state *pci_saved_state;
atomic_t refcnt;
};
#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
#define is_msi(vdev) (vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX)
#define is_msix(vdev) (vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX)
#define is_irq_none(vdev) (!(is_intx(vdev) || is_msi(vdev) || is_msix(vdev)))
#define irq_is(vdev, type) (vdev->irq_type == type)
extern void vfio_pci_intx_mask(struct vfio_pci_device *vdev);
extern void vfio_pci_intx_unmask(struct vfio_pci_device *vdev);
extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev,
uint32_t flags, unsigned index,
unsigned start, unsigned count, void *data);
extern ssize_t vfio_pci_config_readwrite(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,
loff_t *ppos, bool iswrite);
extern int vfio_pci_init_perm_bits(void);
extern void vfio_pci_uninit_perm_bits(void);
extern int vfio_pci_virqfd_init(void);
extern void vfio_pci_virqfd_exit(void);
extern int vfio_config_init(struct vfio_pci_device *vdev);
extern void vfio_config_free(struct vfio_pci_device *vdev);
#endif /* VFIO_PCI_PRIVATE_H */
/*
* VFIO PCI I/O Port & MMIO access
*
* Copyright (C) 2012 Red Hat, Inc. All rights reserved.
* Author: Alex Williamson <alex.williamson@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Derived from original vfio:
* Copyright 2010 Cisco Systems, Inc. All rights reserved.
* Author: Tom Lyon, pugs@cisco.com
*/
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <linux/io.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
* We handle two excluded ranges here as well, if the user tries to read
* the ROM beyond what PCI tells us is available or the MSI-X table region,
* we return 0xFF and writes are dropped.
*/
ssize_t vfio_pci_mem_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;
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) {
size_t fillable, filled;
if (pos < x_start)
fillable = x_start - pos;
else if (pos >= x_end)
fillable = end - pos;
else
fillable = 0;
if (fillable >= 4 && !(pos % 4) && (count >= 4)) {
__le32 val;
if (iswrite) {
if (copy_from_user(&val, buf, 4))
goto out;
iowrite32(le32_to_cpu(val), io + pos);
} else {
val = cpu_to_le32(ioread32(io + pos));
if (copy_to_user(buf, &val, 4))
goto out;
}
filled = 4;
} else if (fillable >= 2 && !(pos % 2) && (count >= 2)) {
__le16 val;
if (iswrite) {
if (copy_from_user(&val, buf, 2))
goto out;
iowrite16(le16_to_cpu(val), io + pos);
} else {
val = cpu_to_le16(ioread16(io + pos));
if (copy_to_user(buf, &val, 2))
goto out;
}
filled = 2;
} else if (fillable) {
u8 val;
if (iswrite) {
if (copy_from_user(&val, buf, 1))
goto out;
iowrite8(val, io + pos);
} else {
val = ioread8(io + pos);
if (copy_to_user(buf, &val, 1))
goto out;
}
filled = 1;
} else {
/* Drop writes, fill reads with FF */
if (!iswrite) {
char val = 0xFF;
size_t i;
for (i = 0; i < x_end - pos; i++) {
if (put_user(val, buf + i))
goto out;
}
}
filled = x_end - pos;
}
count -= filled;
done += filled;
buf += filled;
pos += filled;
}
*ppos += done;
out:
if (bar == PCI_ROM_RESOURCE)
pci_unmap_rom(pdev, io);
return count ? -EFAULT : done;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment