KVM: arm64: Fix HYP idmap unmap when using 52bit PA
[linux-2.6-microblaze.git] / virt / kvm / arm / mmu.c
index 2bc32bd..f7fe724 100644 (file)
@@ -479,7 +479,13 @@ static void unmap_hyp_puds(pgd_t *pgd, phys_addr_t addr, phys_addr_t end)
                clear_hyp_pgd_entry(pgd);
 }
 
-static void unmap_hyp_range(pgd_t *pgdp, phys_addr_t start, u64 size)
+static unsigned int kvm_pgd_index(unsigned long addr, unsigned int ptrs_per_pgd)
+{
+       return (addr >> PGDIR_SHIFT) & (ptrs_per_pgd - 1);
+}
+
+static void __unmap_hyp_range(pgd_t *pgdp, unsigned long ptrs_per_pgd,
+                             phys_addr_t start, u64 size)
 {
        pgd_t *pgd;
        phys_addr_t addr = start, end = start + size;
@@ -489,7 +495,7 @@ static void unmap_hyp_range(pgd_t *pgdp, phys_addr_t start, u64 size)
         * We don't unmap anything from HYP, except at the hyp tear down.
         * Hence, we don't have to invalidate the TLBs here.
         */
-       pgd = pgdp + pgd_index(addr);
+       pgd = pgdp + kvm_pgd_index(addr, ptrs_per_pgd);
        do {
                next = pgd_addr_end(addr, end);
                if (!pgd_none(*pgd))
@@ -497,6 +503,16 @@ static void unmap_hyp_range(pgd_t *pgdp, phys_addr_t start, u64 size)
        } while (pgd++, addr = next, addr != end);
 }
 
+static void unmap_hyp_range(pgd_t *pgdp, phys_addr_t start, u64 size)
+{
+       __unmap_hyp_range(pgdp, PTRS_PER_PGD, start, size);
+}
+
+static void unmap_hyp_idmap_range(pgd_t *pgdp, phys_addr_t start, u64 size)
+{
+       __unmap_hyp_range(pgdp, __kvm_idmap_ptrs_per_pgd(), start, size);
+}
+
 /**
  * free_hyp_pgds - free Hyp-mode page tables
  *
@@ -512,13 +528,13 @@ void free_hyp_pgds(void)
        mutex_lock(&kvm_hyp_pgd_mutex);
 
        if (boot_hyp_pgd) {
-               unmap_hyp_range(boot_hyp_pgd, hyp_idmap_start, PAGE_SIZE);
+               unmap_hyp_idmap_range(boot_hyp_pgd, hyp_idmap_start, PAGE_SIZE);
                free_pages((unsigned long)boot_hyp_pgd, hyp_pgd_order);
                boot_hyp_pgd = NULL;
        }
 
        if (hyp_pgd) {
-               unmap_hyp_range(hyp_pgd, hyp_idmap_start, PAGE_SIZE);
+               unmap_hyp_idmap_range(hyp_pgd, hyp_idmap_start, PAGE_SIZE);
                unmap_hyp_range(hyp_pgd, kern_hyp_va(PAGE_OFFSET),
                                (uintptr_t)high_memory - PAGE_OFFSET);
                unmap_hyp_range(hyp_pgd, kern_hyp_va(VMALLOC_START),
@@ -634,7 +650,7 @@ static int __create_hyp_mappings(pgd_t *pgdp, unsigned long ptrs_per_pgd,
        addr = start & PAGE_MASK;
        end = PAGE_ALIGN(end);
        do {
-               pgd = pgdp + ((addr >> PGDIR_SHIFT) & (ptrs_per_pgd - 1));
+               pgd = pgdp + kvm_pgd_index(addr, ptrs_per_pgd);
 
                if (pgd_none(*pgd)) {
                        pud = pud_alloc_one(NULL, addr);
@@ -713,28 +729,38 @@ int create_hyp_mappings(void *from, void *to, pgprot_t prot)
  * @phys_addr: The physical start address which gets mapped
  * @size:      Size of the region being mapped
  * @kaddr:     Kernel VA for this mapping
- *
- * The resulting HYP VA is the same as the kernel VA, modulo
- * HYP_PAGE_OFFSET.
+ * @haddr:     HYP VA for this mapping
  */
 int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size,
-                          void __iomem **kaddr)
+                          void __iomem **kaddr,
+                          void __iomem **haddr)
 {
        unsigned long start, end;
+       int ret;
 
        *kaddr = ioremap(phys_addr, size);
        if (!*kaddr)
                return -ENOMEM;
 
        if (is_kernel_in_hyp_mode()) {
+               *haddr = *kaddr;
                return 0;
        }
 
 
        start = kern_hyp_va((unsigned long)*kaddr);
        end = kern_hyp_va((unsigned long)*kaddr + size);
-       return __create_hyp_mappings(hyp_pgd, PTRS_PER_PGD, start, end,
+       ret = __create_hyp_mappings(hyp_pgd, PTRS_PER_PGD, start, end,
                                     __phys_to_pfn(phys_addr), PAGE_HYP_DEVICE);
+
+       if (ret) {
+               iounmap(*kaddr);
+               *kaddr = NULL;
+               return ret;
+       }
+
+       *haddr = (void __iomem *)start;
+       return 0;
 }
 
 /**
@@ -1805,7 +1831,9 @@ int kvm_mmu_init(void)
        int err;
 
        hyp_idmap_start = kvm_virt_to_phys(__hyp_idmap_text_start);
+       hyp_idmap_start = ALIGN_DOWN(hyp_idmap_start, PAGE_SIZE);
        hyp_idmap_end = kvm_virt_to_phys(__hyp_idmap_text_end);
+       hyp_idmap_end = ALIGN(hyp_idmap_end, PAGE_SIZE);
        hyp_idmap_vector = kvm_virt_to_phys(__kvm_hyp_init);
 
        /*