diff options
Diffstat (limited to 'virt/kvm/assigned-dev.c')
-rw-r--r-- | virt/kvm/assigned-dev.c | 93 |
1 files changed, 84 insertions, 9 deletions
diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c index 3ad0925d23a9..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,76 @@ 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; | ||
552 | |||
553 | if (!(assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU)) | ||
554 | return -EINVAL; | ||
489 | 555 | ||
490 | mutex_lock(&kvm->lock); | 556 | mutex_lock(&kvm->lock); |
491 | idx = srcu_read_lock(&kvm->srcu); | 557 | idx = srcu_read_lock(&kvm->srcu); |
@@ -513,6 +579,18 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, | |||
513 | r = -EINVAL; | 579 | r = -EINVAL; |
514 | goto out_free; | 580 | goto out_free; |
515 | } | 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 | |||
516 | if (pci_enable_device(dev)) { | 594 | if (pci_enable_device(dev)) { |
517 | printk(KERN_INFO "%s: Could not enable PCI device\n", __func__); | 595 | printk(KERN_INFO "%s: Could not enable PCI device\n", __func__); |
518 | r = -EBUSY; | 596 | r = -EBUSY; |
@@ -544,16 +622,14 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, | |||
544 | 622 | ||
545 | list_add(&match->list, &kvm->arch.assigned_dev_head); | 623 | list_add(&match->list, &kvm->arch.assigned_dev_head); |
546 | 624 | ||
547 | if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) { | 625 | if (!kvm->arch.iommu_domain) { |
548 | if (!kvm->arch.iommu_domain) { | 626 | r = kvm_iommu_map_guest(kvm); |
549 | r = kvm_iommu_map_guest(kvm); | ||
550 | if (r) | ||
551 | goto out_list_del; | ||
552 | } | ||
553 | r = kvm_assign_device(kvm, match); | ||
554 | if (r) | 627 | if (r) |
555 | goto out_list_del; | 628 | goto out_list_del; |
556 | } | 629 | } |
630 | r = kvm_assign_device(kvm, match); | ||
631 | if (r) | ||
632 | goto out_list_del; | ||
557 | 633 | ||
558 | out: | 634 | out: |
559 | srcu_read_unlock(&kvm->srcu, idx); | 635 | srcu_read_unlock(&kvm->srcu, idx); |
@@ -593,8 +669,7 @@ static int kvm_vm_ioctl_deassign_device(struct kvm *kvm, | |||
593 | goto out; | 669 | goto out; |
594 | } | 670 | } |
595 | 671 | ||
596 | if (match->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) | 672 | kvm_deassign_device(kvm, match); |
597 | kvm_deassign_device(kvm, match); | ||
598 | 673 | ||
599 | kvm_free_assigned_device(kvm, match); | 674 | kvm_free_assigned_device(kvm, match); |
600 | 675 | ||