diff options
Diffstat (limited to 'virt/kvm/assigned-dev.c')
-rw-r--r-- | virt/kvm/assigned-dev.c | 95 |
1 files changed, 85 insertions, 10 deletions
diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c index 6cc4b97ec45..af7910228fb 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, |
@@ -474,12 +476,76 @@ out: | |||
474 | return r; | 476 | return r; |
475 | } | 477 | } |
476 | 478 | ||
479 | /* | ||
480 | * We want to test whether the caller has been granted permissions to | ||
481 | * use this device. To be able to configure and control the device, | ||
482 | * the user needs access to PCI configuration space and BAR resources. | ||
483 | * These are accessed through PCI sysfs. PCI config space is often | ||
484 | * passed to the process calling this ioctl via file descriptor, so we | ||
485 | * can't rely on access to that file. We can check for permissions | ||
486 | * on each of the BAR resource files, which is a pretty clear | ||
487 | * indicator that the user has been granted access to the device. | ||
488 | */ | ||
489 | static int probe_sysfs_permissions(struct pci_dev *dev) | ||
490 | { | ||
491 | #ifdef CONFIG_SYSFS | ||
492 | int i; | ||
493 | bool bar_found = false; | ||
494 | |||
495 | for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++) { | ||
496 | char *kpath, *syspath; | ||
497 | struct path path; | ||
498 | struct inode *inode; | ||
499 | int r; | ||
500 | |||
501 | if (!pci_resource_len(dev, i)) | ||
502 | continue; | ||
503 | |||
504 | kpath = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); | ||
505 | if (!kpath) | ||
506 | return -ENOMEM; | ||
507 | |||
508 | /* Per sysfs-rules, sysfs is always at /sys */ | ||
509 | syspath = kasprintf(GFP_KERNEL, "/sys%s/resource%d", kpath, i); | ||
510 | kfree(kpath); | ||
511 | if (!syspath) | ||
512 | return -ENOMEM; | ||
513 | |||
514 | r = kern_path(syspath, LOOKUP_FOLLOW, &path); | ||
515 | kfree(syspath); | ||
516 | if (r) | ||
517 | return r; | ||
518 | |||
519 | inode = path.dentry->d_inode; | ||
520 | |||
521 | r = inode_permission(inode, MAY_READ | MAY_WRITE | MAY_ACCESS); | ||
522 | path_put(&path); | ||
523 | if (r) | ||
524 | return r; | ||
525 | |||
526 | bar_found = true; | ||
527 | } | ||
528 | |||
529 | /* If no resources, probably something special */ | ||
530 | if (!bar_found) | ||
531 | return -EPERM; | ||
532 | |||
533 | return 0; | ||
534 | #else | ||
535 | return -EINVAL; /* No way to control the device without sysfs */ | ||
536 | #endif | ||
537 | } | ||
538 | |||
477 | static int kvm_vm_ioctl_assign_device(struct kvm *kvm, | 539 | static int kvm_vm_ioctl_assign_device(struct kvm *kvm, |
478 | struct kvm_assigned_pci_dev *assigned_dev) | 540 | struct kvm_assigned_pci_dev *assigned_dev) |
479 | { | 541 | { |
480 | int r = 0, idx; | 542 | int r = 0, idx; |
481 | struct kvm_assigned_dev_kernel *match; | 543 | struct kvm_assigned_dev_kernel *match; |
482 | struct pci_dev *dev; | 544 | struct pci_dev *dev; |
545 | u8 header_type; | ||
546 | |||
547 | if (!(assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU)) | ||
548 | return -EINVAL; | ||
483 | 549 | ||
484 | mutex_lock(&kvm->lock); | 550 | mutex_lock(&kvm->lock); |
485 | idx = srcu_read_lock(&kvm->srcu); | 551 | idx = srcu_read_lock(&kvm->srcu); |
@@ -507,6 +573,18 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, | |||
507 | r = -EINVAL; | 573 | r = -EINVAL; |
508 | goto out_free; | 574 | goto out_free; |
509 | } | 575 | } |
576 | |||
577 | /* Don't allow bridges to be assigned */ | ||
578 | pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type); | ||
579 | if ((header_type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL) { | ||
580 | r = -EPERM; | ||
581 | goto out_put; | ||
582 | } | ||
583 | |||
584 | r = probe_sysfs_permissions(dev); | ||
585 | if (r) | ||
586 | goto out_put; | ||
587 | |||
510 | if (pci_enable_device(dev)) { | 588 | if (pci_enable_device(dev)) { |
511 | printk(KERN_INFO "%s: Could not enable PCI device\n", __func__); | 589 | printk(KERN_INFO "%s: Could not enable PCI device\n", __func__); |
512 | r = -EBUSY; | 590 | r = -EBUSY; |
@@ -538,16 +616,14 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm, | |||
538 | 616 | ||
539 | list_add(&match->list, &kvm->arch.assigned_dev_head); | 617 | list_add(&match->list, &kvm->arch.assigned_dev_head); |
540 | 618 | ||
541 | if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) { | 619 | if (!kvm->arch.iommu_domain) { |
542 | if (!kvm->arch.iommu_domain) { | 620 | r = kvm_iommu_map_guest(kvm); |
543 | r = kvm_iommu_map_guest(kvm); | ||
544 | if (r) | ||
545 | goto out_list_del; | ||
546 | } | ||
547 | r = kvm_assign_device(kvm, match); | ||
548 | if (r) | 621 | if (r) |
549 | goto out_list_del; | 622 | goto out_list_del; |
550 | } | 623 | } |
624 | r = kvm_assign_device(kvm, match); | ||
625 | if (r) | ||
626 | goto out_list_del; | ||
551 | 627 | ||
552 | out: | 628 | out: |
553 | srcu_read_unlock(&kvm->srcu, idx); | 629 | srcu_read_unlock(&kvm->srcu, idx); |
@@ -587,8 +663,7 @@ static int kvm_vm_ioctl_deassign_device(struct kvm *kvm, | |||
587 | goto out; | 663 | goto out; |
588 | } | 664 | } |
589 | 665 | ||
590 | if (match->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) | 666 | kvm_deassign_device(kvm, match); |
591 | kvm_deassign_device(kvm, match); | ||
592 | 667 | ||
593 | kvm_free_assigned_device(kvm, match); | 668 | kvm_free_assigned_device(kvm, match); |
594 | 669 | ||
@@ -617,7 +692,7 @@ static int kvm_vm_ioctl_set_msix_nr(struct kvm *kvm, | |||
617 | if (adev->entries_nr == 0) { | 692 | if (adev->entries_nr == 0) { |
618 | adev->entries_nr = entry_nr->entry_nr; | 693 | adev->entries_nr = entry_nr->entry_nr; |
619 | if (adev->entries_nr == 0 || | 694 | if (adev->entries_nr == 0 || |
620 | adev->entries_nr >= KVM_MAX_MSIX_PER_DEV) { | 695 | adev->entries_nr > KVM_MAX_MSIX_PER_DEV) { |
621 | r = -EINVAL; | 696 | r = -EINVAL; |
622 | goto msix_nr_out; | 697 | goto msix_nr_out; |
623 | } | 698 | } |