]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - virt/kvm/kvm_main.c
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
[karo-tx-linux.git] / virt / kvm / kvm_main.c
index 8fd325a0a1b1a13b7f4a8c8d4a2854caaac1144e..ae88b719bd2e477bbdb729f0b55555ebc869fcef 100644 (file)
@@ -1535,21 +1535,38 @@ int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data,
 }
 
 int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
-                             gpa_t gpa)
+                             gpa_t gpa, unsigned long len)
 {
        struct kvm_memslots *slots = kvm_memslots(kvm);
        int offset = offset_in_page(gpa);
-       gfn_t gfn = gpa >> PAGE_SHIFT;
+       gfn_t start_gfn = gpa >> PAGE_SHIFT;
+       gfn_t end_gfn = (gpa + len - 1) >> PAGE_SHIFT;
+       gfn_t nr_pages_needed = end_gfn - start_gfn + 1;
+       gfn_t nr_pages_avail;
 
        ghc->gpa = gpa;
        ghc->generation = slots->generation;
-       ghc->memslot = gfn_to_memslot(kvm, gfn);
-       ghc->hva = gfn_to_hva_many(ghc->memslot, gfn, NULL);
-       if (!kvm_is_error_hva(ghc->hva))
+       ghc->len = len;
+       ghc->memslot = gfn_to_memslot(kvm, start_gfn);
+       ghc->hva = gfn_to_hva_many(ghc->memslot, start_gfn, &nr_pages_avail);
+       if (!kvm_is_error_hva(ghc->hva) && nr_pages_avail >= nr_pages_needed) {
                ghc->hva += offset;
-       else
-               return -EFAULT;
-
+       } else {
+               /*
+                * If the requested region crosses two memslots, we still
+                * verify that the entire region is valid here.
+                */
+               while (start_gfn <= end_gfn) {
+                       ghc->memslot = gfn_to_memslot(kvm, start_gfn);
+                       ghc->hva = gfn_to_hva_many(ghc->memslot, start_gfn,
+                                                  &nr_pages_avail);
+                       if (kvm_is_error_hva(ghc->hva))
+                               return -EFAULT;
+                       start_gfn += nr_pages_avail;
+               }
+               /* Use the slow path for cross page reads and writes. */
+               ghc->memslot = NULL;
+       }
        return 0;
 }
 EXPORT_SYMBOL_GPL(kvm_gfn_to_hva_cache_init);
@@ -1560,8 +1577,13 @@ int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
        struct kvm_memslots *slots = kvm_memslots(kvm);
        int r;
 
+       BUG_ON(len > ghc->len);
+
        if (slots->generation != ghc->generation)
-               kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa);
+               kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa, ghc->len);
+
+       if (unlikely(!ghc->memslot))
+               return kvm_write_guest(kvm, ghc->gpa, data, len);
 
        if (kvm_is_error_hva(ghc->hva))
                return -EFAULT;
@@ -1581,8 +1603,13 @@ int kvm_read_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
        struct kvm_memslots *slots = kvm_memslots(kvm);
        int r;
 
+       BUG_ON(len > ghc->len);
+
        if (slots->generation != ghc->generation)
-               kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa);
+               kvm_gfn_to_hva_cache_init(kvm, ghc, ghc->gpa, ghc->len);
+
+       if (unlikely(!ghc->memslot))
+               return kvm_read_guest(kvm, ghc->gpa, data, len);
 
        if (kvm_is_error_hva(ghc->hva))
                return -EFAULT;
@@ -1951,7 +1978,7 @@ static long kvm_vcpu_ioctl(struct file *filp,
        if (vcpu->kvm->mm != current->mm)
                return -EIO;
 
-#if defined(CONFIG_S390) || defined(CONFIG_PPC)
+#if defined(CONFIG_S390) || defined(CONFIG_PPC) || defined(CONFIG_MIPS)
        /*
         * Special cases: vcpu ioctls that are asynchronous to vcpu execution,
         * so vcpu_load() would break it.