diff options
author | Christoffer Dall <christoffer.dall@linaro.org> | 2013-11-15 16:14:12 -0500 |
---|---|---|
committer | Christoffer Dall <christoffer.dall@linaro.org> | 2013-11-16 21:54:45 -0500 |
commit | 40c2729bab48e2832b17c1fa8af9db60e776131b (patch) | |
tree | a87b795e3d512d059c099beb1c1af8b28e4ba90f /arch | |
parent | 5e01dc7b26d9f24f39abace5da98ccbd6a5ceb52 (diff) |
arm/arm64: KVM: Fix hyp mappings of vmalloc regions
Using virt_to_phys on percpu mappings is horribly wrong as it may be
backed by vmalloc. Introduce kvm_kaddr_to_phys which translates both
types of valid kernel addresses to the corresponding physical address.
At the same time resolves a typing issue where we were storing the
physical address as a 32 bit unsigned long (on arm), truncating the
physical address for addresses above the 4GB limit. This caused
breakage on Keystone.
Cc: <stable@vger.kernel.org> [3.10+]
Reported-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Tested-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/kvm/mmu.c | 34 |
1 files changed, 28 insertions, 6 deletions
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index b0de86b56c13..cb79a5dd6d96 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c | |||
@@ -307,6 +307,17 @@ out: | |||
307 | return err; | 307 | return err; |
308 | } | 308 | } |
309 | 309 | ||
310 | static phys_addr_t kvm_kaddr_to_phys(void *kaddr) | ||
311 | { | ||
312 | if (!is_vmalloc_addr(kaddr)) { | ||
313 | BUG_ON(!virt_addr_valid(kaddr)); | ||
314 | return __pa(kaddr); | ||
315 | } else { | ||
316 | return page_to_phys(vmalloc_to_page(kaddr)) + | ||
317 | offset_in_page(kaddr); | ||
318 | } | ||
319 | } | ||
320 | |||
310 | /** | 321 | /** |
311 | * create_hyp_mappings - duplicate a kernel virtual address range in Hyp mode | 322 | * create_hyp_mappings - duplicate a kernel virtual address range in Hyp mode |
312 | * @from: The virtual kernel start address of the range | 323 | * @from: The virtual kernel start address of the range |
@@ -318,16 +329,27 @@ out: | |||
318 | */ | 329 | */ |
319 | int create_hyp_mappings(void *from, void *to) | 330 | int create_hyp_mappings(void *from, void *to) |
320 | { | 331 | { |
321 | unsigned long phys_addr = virt_to_phys(from); | 332 | phys_addr_t phys_addr; |
333 | unsigned long virt_addr; | ||
322 | unsigned long start = KERN_TO_HYP((unsigned long)from); | 334 | unsigned long start = KERN_TO_HYP((unsigned long)from); |
323 | unsigned long end = KERN_TO_HYP((unsigned long)to); | 335 | unsigned long end = KERN_TO_HYP((unsigned long)to); |
324 | 336 | ||
325 | /* Check for a valid kernel memory mapping */ | 337 | start = start & PAGE_MASK; |
326 | if (!virt_addr_valid(from) || !virt_addr_valid(to - 1)) | 338 | end = PAGE_ALIGN(end); |
327 | return -EINVAL; | ||
328 | 339 | ||
329 | return __create_hyp_mappings(hyp_pgd, start, end, | 340 | for (virt_addr = start; virt_addr < end; virt_addr += PAGE_SIZE) { |
330 | __phys_to_pfn(phys_addr), PAGE_HYP); | 341 | int err; |
342 | |||
343 | phys_addr = kvm_kaddr_to_phys(from + virt_addr - start); | ||
344 | err = __create_hyp_mappings(hyp_pgd, virt_addr, | ||
345 | virt_addr + PAGE_SIZE, | ||
346 | __phys_to_pfn(phys_addr), | ||
347 | PAGE_HYP); | ||
348 | if (err) | ||
349 | return err; | ||
350 | } | ||
351 | |||
352 | return 0; | ||
331 | } | 353 | } |
332 | 354 | ||
333 | /** | 355 | /** |