diff options
Diffstat (limited to 'virt')
-rw-r--r-- | virt/kvm/assigned-dev.c | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c index a251a28f79c7..758e3b36d4cf 100644 --- a/virt/kvm/assigned-dev.c +++ b/virt/kvm/assigned-dev.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/pci.h> | 17 | #include <linux/pci.h> |
18 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/namei.h> | ||
21 | #include <linux/fs.h> | ||
20 | #include "irq.h" | 22 | #include "irq.h" |
21 | 23 | ||
22 | static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct list_head *head, | 24 | static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct list_head *head, |
@@ -480,12 +482,73 @@ out: | |||
480 | return r; | 482 | return r; |
481 | } | 483 | } |
482 | 484 | ||
485 | /* | ||
486 | * We want to test whether the caller has been granted permissions to | ||
487 | * use this device. To be able to configure and control the device, | ||
488 | * the user needs access to PCI configuration space and BAR resources. | ||
489 | * These are accessed through PCI sysfs. PCI config space is often | ||
490 | * passed to the process calling this ioctl via file descriptor, so we | ||
491 | * can't rely on access to that file. We can check for permissions | ||
492 | * on each of the BAR resource files, which is a pretty clear | ||
493 | * indicator that the user has been granted access to the device. | ||
494 | */ | ||
495 | static int probe_sysfs_permissions(struct pci_dev *dev) | ||
496 | { | ||
497 | #ifdef CONFIG_SYSFS | ||
498 | int i; | ||
499 | bool bar_found = false; | ||
500 | |||
501 | for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++) { | ||
502 | char *kpath, *syspath; | ||
503 | struct path path; | ||
504 | struct inode *inode; | ||
505 | int r; | ||
506 | |||
507 | if (!pci_resource_len(dev, i)) | ||
508 | continue; | ||
509 | |||
510 | kpath = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); | ||
511 | if (!kpath) | ||
512 | return -ENOMEM; | ||
513 | |||
514 | /* Per sysfs-rules, sysfs is always at /sys */ | ||
515 | syspath = kasprintf(GFP_KERNEL, "/sys%s/resource%d", kpath, i); | ||
516 | kfree(kpath); | ||
517 | if (!syspath) | ||
518 | return -ENOMEM; | ||
519 | |||
520 | r = kern_path(syspath, LOOKUP_FOLLOW, &path); | ||
521 | kfree(syspath); | ||
522 | if (r) | ||
523 | return r; | ||
524 | |||
525 | inode = path.dentry->d_inode; | ||
526 | |||
527 | r = inode_permission(inode, MAY_READ | MAY_WRITE | MAY_ACCESS); | ||
528 | path_put(&path); | ||
529 | if (r) | ||
530 | return r; | ||
531 | |||
532 | bar_found = true; | ||
533 | } | ||
534 | |||
535 | /* If no resources, probably something special */ | ||
536 | if (!bar_found) | ||
537 | return -EPERM; | ||
538 | |||
539 | return 0; | ||
540 | #else | ||
541 | return -EINVAL; /* No way to control the device without sysfs */ | ||
542 | #endif | ||
543 | } | ||
544 | |||
483 | static int kvm_vm_ioctl_assign_device(struct kvm *kvm, | 545 | static int kvm_vm_ioctl_assign_device(struct kvm *kvm, |
484 | struct kvm_assigned_pci_dev *assigned_dev) | 546 | struct kvm_assigned_pci_dev *assigned_dev) |
485 | { | 547 | { |
486 | int r = 0, idx; | 548 | int r = 0, idx; |
487 | struct kvm_assigned_dev_kernel *match; | 549 | struct kvm_assigned_dev_kernel *match; |
488 | struct pci_dev *dev; | 550 | struct pci_dev *dev; |
551 | u8 header_type; | ||
489 | 552 | ||
490 | if (!(assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU)) | 553 | if (!(assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU)) |
491 | return -EINVAL; | 554 | return -EINVAL; |
@@ -516,6 +579,18 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, | |||
516 | r = -EINVAL; | 579 | r = -EINVAL; |
517 | goto out_free; | 580 | goto out_free; |
518 | } | 581 | } |
582 | |||
583 | /* Don't allow bridges to be assigned */ | ||
584 | pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); | ||
585 | if ((header_type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL) { | ||
586 | r = -EPERM; | ||
587 | goto out_put; | ||
588 | } | ||
589 | |||
590 | r = probe_sysfs_permissions(dev); | ||
591 | if (r) | ||
592 | goto out_put; | ||
593 | |||
519 | if (pci_enable_device(dev)) { | 594 | if (pci_enable_device(dev)) { |
520 | printk(KERN_INFO "%s: Could not enable PCI device\n", __func__); | 595 | printk(KERN_INFO "%s: Could not enable PCI device\n", __func__); |
521 | r = -EBUSY; | 596 | r = -EBUSY; |