diff options
Diffstat (limited to 'virt')
-rw-r--r-- | virt/kvm/kvm_main.c | 64 |
1 files changed, 44 insertions, 20 deletions
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index a83ca63d26fc..64c5dc37c6a1 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -719,6 +719,24 @@ static struct kvm_memslots *install_new_memslots(struct kvm *kvm, | |||
719 | } | 719 | } |
720 | 720 | ||
721 | /* | 721 | /* |
722 | * KVM_SET_USER_MEMORY_REGION ioctl allows the following operations: | ||
723 | * - create a new memory slot | ||
724 | * - delete an existing memory slot | ||
725 | * - modify an existing memory slot | ||
726 | * -- move it in the guest physical memory space | ||
727 | * -- just change its flags | ||
728 | * | ||
729 | * Since flags can be changed by some of these operations, the following | ||
730 | * differentiation is the best we can do for __kvm_set_memory_region(): | ||
731 | */ | ||
732 | enum kvm_mr_change { | ||
733 | KVM_MR_CREATE, | ||
734 | KVM_MR_DELETE, | ||
735 | KVM_MR_MOVE, | ||
736 | KVM_MR_FLAGS_ONLY, | ||
737 | }; | ||
738 | |||
739 | /* | ||
722 | * Allocate some memory and give it an address in the guest physical address | 740 | * Allocate some memory and give it an address in the guest physical address |
723 | * space. | 741 | * space. |
724 | * | 742 | * |
@@ -737,6 +755,7 @@ int __kvm_set_memory_region(struct kvm *kvm, | |||
737 | struct kvm_memory_slot old, new; | 755 | struct kvm_memory_slot old, new; |
738 | struct kvm_memslots *slots = NULL, *old_memslots; | 756 | struct kvm_memslots *slots = NULL, *old_memslots; |
739 | bool old_iommu_mapped; | 757 | bool old_iommu_mapped; |
758 | enum kvm_mr_change change; | ||
740 | 759 | ||
741 | r = check_memory_region_flags(mem); | 760 | r = check_memory_region_flags(mem); |
742 | if (r) | 761 | if (r) |
@@ -780,17 +799,30 @@ int __kvm_set_memory_region(struct kvm *kvm, | |||
780 | 799 | ||
781 | old_iommu_mapped = old.npages; | 800 | old_iommu_mapped = old.npages; |
782 | 801 | ||
783 | /* | ||
784 | * Disallow changing a memory slot's size or changing anything about | ||
785 | * zero sized slots that doesn't involve making them non-zero. | ||
786 | */ | ||
787 | r = -EINVAL; | 802 | r = -EINVAL; |
788 | if (npages && old.npages && npages != old.npages) | 803 | if (npages) { |
789 | goto out; | 804 | if (!old.npages) |
790 | if (!npages && !old.npages) | 805 | change = KVM_MR_CREATE; |
806 | else { /* Modify an existing slot. */ | ||
807 | if ((mem->userspace_addr != old.userspace_addr) || | ||
808 | (npages != old.npages)) | ||
809 | goto out; | ||
810 | |||
811 | if (base_gfn != old.base_gfn) | ||
812 | change = KVM_MR_MOVE; | ||
813 | else if (new.flags != old.flags) | ||
814 | change = KVM_MR_FLAGS_ONLY; | ||
815 | else { /* Nothing to change. */ | ||
816 | r = 0; | ||
817 | goto out; | ||
818 | } | ||
819 | } | ||
820 | } else if (old.npages) { | ||
821 | change = KVM_MR_DELETE; | ||
822 | } else /* Modify a non-existent slot: disallowed. */ | ||
791 | goto out; | 823 | goto out; |
792 | 824 | ||
793 | if ((npages && !old.npages) || (base_gfn != old.base_gfn)) { | 825 | if ((change == KVM_MR_CREATE) || (change == KVM_MR_MOVE)) { |
794 | /* Check for overlaps */ | 826 | /* Check for overlaps */ |
795 | r = -EEXIST; | 827 | r = -EEXIST; |
796 | kvm_for_each_memslot(slot, kvm->memslots) { | 828 | kvm_for_each_memslot(slot, kvm->memslots) { |
@@ -808,20 +840,12 @@ int __kvm_set_memory_region(struct kvm *kvm, | |||
808 | new.dirty_bitmap = NULL; | 840 | new.dirty_bitmap = NULL; |
809 | 841 | ||
810 | r = -ENOMEM; | 842 | r = -ENOMEM; |
811 | 843 | if (change == KVM_MR_CREATE) { | |
812 | /* | ||
813 | * Allocate if a slot is being created. If modifying a slot, | ||
814 | * the userspace_addr cannot change. | ||
815 | */ | ||
816 | if (!old.npages) { | ||
817 | new.user_alloc = user_alloc; | 844 | new.user_alloc = user_alloc; |
818 | new.userspace_addr = mem->userspace_addr; | 845 | new.userspace_addr = mem->userspace_addr; |
819 | 846 | ||
820 | if (kvm_arch_create_memslot(&new, npages)) | 847 | if (kvm_arch_create_memslot(&new, npages)) |
821 | goto out_free; | 848 | goto out_free; |
822 | } else if (npages && mem->userspace_addr != old.userspace_addr) { | ||
823 | r = -EINVAL; | ||
824 | goto out_free; | ||
825 | } | 849 | } |
826 | 850 | ||
827 | /* Allocate page dirty bitmap if needed */ | 851 | /* Allocate page dirty bitmap if needed */ |
@@ -830,7 +854,7 @@ int __kvm_set_memory_region(struct kvm *kvm, | |||
830 | goto out_free; | 854 | goto out_free; |
831 | } | 855 | } |
832 | 856 | ||
833 | if (!npages || base_gfn != old.base_gfn) { | 857 | if ((change == KVM_MR_DELETE) || (change == KVM_MR_MOVE)) { |
834 | r = -ENOMEM; | 858 | r = -ENOMEM; |
835 | slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots), | 859 | slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots), |
836 | GFP_KERNEL); | 860 | GFP_KERNEL); |
@@ -881,7 +905,7 @@ int __kvm_set_memory_region(struct kvm *kvm, | |||
881 | * slots (size changes, userspace addr changes) is disallowed above, | 905 | * slots (size changes, userspace addr changes) is disallowed above, |
882 | * so any other attribute changes getting here can be skipped. | 906 | * so any other attribute changes getting here can be skipped. |
883 | */ | 907 | */ |
884 | if (npages) { | 908 | if (change != KVM_MR_DELETE) { |
885 | if (old_iommu_mapped && | 909 | if (old_iommu_mapped && |
886 | ((new.flags ^ old.flags) & KVM_MEM_READONLY)) { | 910 | ((new.flags ^ old.flags) & KVM_MEM_READONLY)) { |
887 | kvm_iommu_unmap_pages(kvm, &old); | 911 | kvm_iommu_unmap_pages(kvm, &old); |
@@ -896,7 +920,7 @@ int __kvm_set_memory_region(struct kvm *kvm, | |||
896 | } | 920 | } |
897 | 921 | ||
898 | /* actual memory is freed via old in kvm_free_physmem_slot below */ | 922 | /* actual memory is freed via old in kvm_free_physmem_slot below */ |
899 | if (!npages) { | 923 | if (change == KVM_MR_DELETE) { |
900 | new.dirty_bitmap = NULL; | 924 | new.dirty_bitmap = NULL; |
901 | memset(&new.arch, 0, sizeof(new.arch)); | 925 | memset(&new.arch, 0, sizeof(new.arch)); |
902 | } | 926 | } |