diff options
Diffstat (limited to 'arch/arm/kvm')
-rw-r--r-- | arch/arm/kvm/arm.c | 172 | ||||
-rw-r--r-- | arch/arm/kvm/init.S | 95 | ||||
-rw-r--r-- | arch/arm/kvm/interrupts.S | 62 | ||||
-rw-r--r-- | arch/arm/kvm/mmu.c | 248 |
4 files changed, 577 insertions, 0 deletions
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index d3506b4001aa..2c6b780e78a7 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c | |||
@@ -34,11 +34,21 @@ | |||
34 | #include <asm/ptrace.h> | 34 | #include <asm/ptrace.h> |
35 | #include <asm/mman.h> | 35 | #include <asm/mman.h> |
36 | #include <asm/cputype.h> | 36 | #include <asm/cputype.h> |
37 | #include <asm/tlbflush.h> | ||
38 | #include <asm/virt.h> | ||
39 | #include <asm/kvm_arm.h> | ||
40 | #include <asm/kvm_asm.h> | ||
41 | #include <asm/kvm_mmu.h> | ||
37 | 42 | ||
38 | #ifdef REQUIRES_VIRT | 43 | #ifdef REQUIRES_VIRT |
39 | __asm__(".arch_extension virt"); | 44 | __asm__(".arch_extension virt"); |
40 | #endif | 45 | #endif |
41 | 46 | ||
47 | static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); | ||
48 | static struct vfp_hard_struct __percpu *kvm_host_vfp_state; | ||
49 | static unsigned long hyp_default_vectors; | ||
50 | |||
51 | |||
42 | int kvm_arch_hardware_enable(void *garbage) | 52 | int kvm_arch_hardware_enable(void *garbage) |
43 | { | 53 | { |
44 | return 0; | 54 | return 0; |
@@ -331,9 +341,171 @@ long kvm_arch_vm_ioctl(struct file *filp, | |||
331 | return -EINVAL; | 341 | return -EINVAL; |
332 | } | 342 | } |
333 | 343 | ||
344 | static void cpu_init_hyp_mode(void *vector) | ||
345 | { | ||
346 | unsigned long long pgd_ptr; | ||
347 | unsigned long pgd_low, pgd_high; | ||
348 | unsigned long hyp_stack_ptr; | ||
349 | unsigned long stack_page; | ||
350 | unsigned long vector_ptr; | ||
351 | |||
352 | /* Switch from the HYP stub to our own HYP init vector */ | ||
353 | __hyp_set_vectors((unsigned long)vector); | ||
354 | |||
355 | pgd_ptr = (unsigned long long)kvm_mmu_get_httbr(); | ||
356 | pgd_low = (pgd_ptr & ((1ULL << 32) - 1)); | ||
357 | pgd_high = (pgd_ptr >> 32ULL); | ||
358 | stack_page = __get_cpu_var(kvm_arm_hyp_stack_page); | ||
359 | hyp_stack_ptr = stack_page + PAGE_SIZE; | ||
360 | vector_ptr = (unsigned long)__kvm_hyp_vector; | ||
361 | |||
362 | /* | ||
363 | * Call initialization code, and switch to the full blown | ||
364 | * HYP code. The init code doesn't need to preserve these registers as | ||
365 | * r1-r3 and r12 are already callee save according to the AAPCS. | ||
366 | * Note that we slightly misuse the prototype by casing the pgd_low to | ||
367 | * a void *. | ||
368 | */ | ||
369 | kvm_call_hyp((void *)pgd_low, pgd_high, hyp_stack_ptr, vector_ptr); | ||
370 | } | ||
371 | |||
372 | /** | ||
373 | * Inits Hyp-mode on all online CPUs | ||
374 | */ | ||
375 | static int init_hyp_mode(void) | ||
376 | { | ||
377 | phys_addr_t init_phys_addr; | ||
378 | int cpu; | ||
379 | int err = 0; | ||
380 | |||
381 | /* | ||
382 | * Allocate Hyp PGD and setup Hyp identity mapping | ||
383 | */ | ||
384 | err = kvm_mmu_init(); | ||
385 | if (err) | ||
386 | goto out_err; | ||
387 | |||
388 | /* | ||
389 | * It is probably enough to obtain the default on one | ||
390 | * CPU. It's unlikely to be different on the others. | ||
391 | */ | ||
392 | hyp_default_vectors = __hyp_get_vectors(); | ||
393 | |||
394 | /* | ||
395 | * Allocate stack pages for Hypervisor-mode | ||
396 | */ | ||
397 | for_each_possible_cpu(cpu) { | ||
398 | unsigned long stack_page; | ||
399 | |||
400 | stack_page = __get_free_page(GFP_KERNEL); | ||
401 | if (!stack_page) { | ||
402 | err = -ENOMEM; | ||
403 | goto out_free_stack_pages; | ||
404 | } | ||
405 | |||
406 | per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page; | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * Execute the init code on each CPU. | ||
411 | * | ||
412 | * Note: The stack is not mapped yet, so don't do anything else than | ||
413 | * initializing the hypervisor mode on each CPU using a local stack | ||
414 | * space for temporary storage. | ||
415 | */ | ||
416 | init_phys_addr = virt_to_phys(__kvm_hyp_init); | ||
417 | for_each_online_cpu(cpu) { | ||
418 | smp_call_function_single(cpu, cpu_init_hyp_mode, | ||
419 | (void *)(long)init_phys_addr, 1); | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * Unmap the identity mapping | ||
424 | */ | ||
425 | kvm_clear_hyp_idmap(); | ||
426 | |||
427 | /* | ||
428 | * Map the Hyp-code called directly from the host | ||
429 | */ | ||
430 | err = create_hyp_mappings(__kvm_hyp_code_start, __kvm_hyp_code_end); | ||
431 | if (err) { | ||
432 | kvm_err("Cannot map world-switch code\n"); | ||
433 | goto out_free_mappings; | ||
434 | } | ||
435 | |||
436 | /* | ||
437 | * Map the Hyp stack pages | ||
438 | */ | ||
439 | for_each_possible_cpu(cpu) { | ||
440 | char *stack_page = (char *)per_cpu(kvm_arm_hyp_stack_page, cpu); | ||
441 | err = create_hyp_mappings(stack_page, stack_page + PAGE_SIZE); | ||
442 | |||
443 | if (err) { | ||
444 | kvm_err("Cannot map hyp stack\n"); | ||
445 | goto out_free_mappings; | ||
446 | } | ||
447 | } | ||
448 | |||
449 | /* | ||
450 | * Map the host VFP structures | ||
451 | */ | ||
452 | kvm_host_vfp_state = alloc_percpu(struct vfp_hard_struct); | ||
453 | if (!kvm_host_vfp_state) { | ||
454 | err = -ENOMEM; | ||
455 | kvm_err("Cannot allocate host VFP state\n"); | ||
456 | goto out_free_mappings; | ||
457 | } | ||
458 | |||
459 | for_each_possible_cpu(cpu) { | ||
460 | struct vfp_hard_struct *vfp; | ||
461 | |||
462 | vfp = per_cpu_ptr(kvm_host_vfp_state, cpu); | ||
463 | err = create_hyp_mappings(vfp, vfp + 1); | ||
464 | |||
465 | if (err) { | ||
466 | kvm_err("Cannot map host VFP state: %d\n", err); | ||
467 | goto out_free_vfp; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | kvm_info("Hyp mode initialized successfully\n"); | ||
472 | return 0; | ||
473 | out_free_vfp: | ||
474 | free_percpu(kvm_host_vfp_state); | ||
475 | out_free_mappings: | ||
476 | free_hyp_pmds(); | ||
477 | out_free_stack_pages: | ||
478 | for_each_possible_cpu(cpu) | ||
479 | free_page(per_cpu(kvm_arm_hyp_stack_page, cpu)); | ||
480 | out_err: | ||
481 | kvm_err("error initializing Hyp mode: %d\n", err); | ||
482 | return err; | ||
483 | } | ||
484 | |||
485 | /** | ||
486 | * Initialize Hyp-mode and memory mappings on all CPUs. | ||
487 | */ | ||
334 | int kvm_arch_init(void *opaque) | 488 | int kvm_arch_init(void *opaque) |
335 | { | 489 | { |
490 | int err; | ||
491 | |||
492 | if (!is_hyp_mode_available()) { | ||
493 | kvm_err("HYP mode not available\n"); | ||
494 | return -ENODEV; | ||
495 | } | ||
496 | |||
497 | if (kvm_target_cpu() < 0) { | ||
498 | kvm_err("Target CPU not supported!\n"); | ||
499 | return -ENODEV; | ||
500 | } | ||
501 | |||
502 | err = init_hyp_mode(); | ||
503 | if (err) | ||
504 | goto out_err; | ||
505 | |||
336 | return 0; | 506 | return 0; |
507 | out_err: | ||
508 | return err; | ||
337 | } | 509 | } |
338 | 510 | ||
339 | /* NOP: Compiling as a module not supported */ | 511 | /* NOP: Compiling as a module not supported */ |
diff --git a/arch/arm/kvm/init.S b/arch/arm/kvm/init.S index 1dc8926e26d2..9f37a79b880b 100644 --- a/arch/arm/kvm/init.S +++ b/arch/arm/kvm/init.S | |||
@@ -15,5 +15,100 @@ | |||
15 | * along with this program; if not, write to the Free Software | 15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | */ | 17 | */ |
18 | |||
19 | #include <linux/linkage.h> | ||
20 | #include <asm/unified.h> | ||
18 | #include <asm/asm-offsets.h> | 21 | #include <asm/asm-offsets.h> |
19 | #include <asm/kvm_asm.h> | 22 | #include <asm/kvm_asm.h> |
23 | #include <asm/kvm_arm.h> | ||
24 | |||
25 | /******************************************************************** | ||
26 | * Hypervisor initialization | ||
27 | * - should be called with: | ||
28 | * r0,r1 = Hypervisor pgd pointer | ||
29 | * r2 = top of Hyp stack (kernel VA) | ||
30 | * r3 = pointer to hyp vectors | ||
31 | */ | ||
32 | |||
33 | .text | ||
34 | .pushsection .hyp.idmap.text,"ax" | ||
35 | .align 5 | ||
36 | __kvm_hyp_init: | ||
37 | .globl __kvm_hyp_init | ||
38 | |||
39 | @ Hyp-mode exception vector | ||
40 | W(b) . | ||
41 | W(b) . | ||
42 | W(b) . | ||
43 | W(b) . | ||
44 | W(b) . | ||
45 | W(b) __do_hyp_init | ||
46 | W(b) . | ||
47 | W(b) . | ||
48 | |||
49 | __do_hyp_init: | ||
50 | @ Set the HTTBR to point to the hypervisor PGD pointer passed | ||
51 | mcrr p15, 4, r0, r1, c2 | ||
52 | |||
53 | @ Set the HTCR and VTCR to the same shareability and cacheability | ||
54 | @ settings as the non-secure TTBCR and with T0SZ == 0. | ||
55 | mrc p15, 4, r0, c2, c0, 2 @ HTCR | ||
56 | ldr r12, =HTCR_MASK | ||
57 | bic r0, r0, r12 | ||
58 | mrc p15, 0, r1, c2, c0, 2 @ TTBCR | ||
59 | and r1, r1, #(HTCR_MASK & ~TTBCR_T0SZ) | ||
60 | orr r0, r0, r1 | ||
61 | mcr p15, 4, r0, c2, c0, 2 @ HTCR | ||
62 | |||
63 | mrc p15, 4, r1, c2, c1, 2 @ VTCR | ||
64 | ldr r12, =VTCR_MASK | ||
65 | bic r1, r1, r12 | ||
66 | bic r0, r0, #(~VTCR_HTCR_SH) @ clear non-reusable HTCR bits | ||
67 | orr r1, r0, r1 | ||
68 | orr r1, r1, #(KVM_VTCR_SL0 | KVM_VTCR_T0SZ | KVM_VTCR_S) | ||
69 | mcr p15, 4, r1, c2, c1, 2 @ VTCR | ||
70 | |||
71 | @ Use the same memory attributes for hyp. accesses as the kernel | ||
72 | @ (copy MAIRx ro HMAIRx). | ||
73 | mrc p15, 0, r0, c10, c2, 0 | ||
74 | mcr p15, 4, r0, c10, c2, 0 | ||
75 | mrc p15, 0, r0, c10, c2, 1 | ||
76 | mcr p15, 4, r0, c10, c2, 1 | ||
77 | |||
78 | @ Set the HSCTLR to: | ||
79 | @ - ARM/THUMB exceptions: Kernel config (Thumb-2 kernel) | ||
80 | @ - Endianness: Kernel config | ||
81 | @ - Fast Interrupt Features: Kernel config | ||
82 | @ - Write permission implies XN: disabled | ||
83 | @ - Instruction cache: enabled | ||
84 | @ - Data/Unified cache: enabled | ||
85 | @ - Memory alignment checks: enabled | ||
86 | @ - MMU: enabled (this code must be run from an identity mapping) | ||
87 | mrc p15, 4, r0, c1, c0, 0 @ HSCR | ||
88 | ldr r12, =HSCTLR_MASK | ||
89 | bic r0, r0, r12 | ||
90 | mrc p15, 0, r1, c1, c0, 0 @ SCTLR | ||
91 | ldr r12, =(HSCTLR_EE | HSCTLR_FI | HSCTLR_I | HSCTLR_C) | ||
92 | and r1, r1, r12 | ||
93 | ARM( ldr r12, =(HSCTLR_M | HSCTLR_A) ) | ||
94 | THUMB( ldr r12, =(HSCTLR_M | HSCTLR_A | HSCTLR_TE) ) | ||
95 | orr r1, r1, r12 | ||
96 | orr r0, r0, r1 | ||
97 | isb | ||
98 | mcr p15, 4, r0, c1, c0, 0 @ HSCR | ||
99 | isb | ||
100 | |||
101 | @ Set stack pointer and return to the kernel | ||
102 | mov sp, r2 | ||
103 | |||
104 | @ Set HVBAR to point to the HYP vectors | ||
105 | mcr p15, 4, r3, c12, c0, 0 @ HVBAR | ||
106 | |||
107 | eret | ||
108 | |||
109 | .ltorg | ||
110 | |||
111 | .globl __kvm_hyp_init_end | ||
112 | __kvm_hyp_init_end: | ||
113 | |||
114 | .popsection | ||
diff --git a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S index 1dc8926e26d2..d10a8075409a 100644 --- a/arch/arm/kvm/interrupts.S +++ b/arch/arm/kvm/interrupts.S | |||
@@ -15,5 +15,67 @@ | |||
15 | * along with this program; if not, write to the Free Software | 15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | */ | 17 | */ |
18 | |||
19 | #include <linux/linkage.h> | ||
20 | #include <linux/const.h> | ||
21 | #include <asm/unified.h> | ||
22 | #include <asm/page.h> | ||
18 | #include <asm/asm-offsets.h> | 23 | #include <asm/asm-offsets.h> |
19 | #include <asm/kvm_asm.h> | 24 | #include <asm/kvm_asm.h> |
25 | #include <asm/kvm_arm.h> | ||
26 | |||
27 | .text | ||
28 | |||
29 | __kvm_hyp_code_start: | ||
30 | .globl __kvm_hyp_code_start | ||
31 | |||
32 | /******************************************************************** | ||
33 | * Flush per-VMID TLBs | ||
34 | */ | ||
35 | ENTRY(__kvm_flush_vm_context) | ||
36 | bx lr | ||
37 | ENDPROC(__kvm_flush_vm_context) | ||
38 | |||
39 | /******************************************************************** | ||
40 | * Hypervisor world-switch code | ||
41 | */ | ||
42 | ENTRY(__kvm_vcpu_run) | ||
43 | bx lr | ||
44 | |||
45 | /******************************************************************** | ||
46 | * Call function in Hyp mode | ||
47 | * | ||
48 | * | ||
49 | * u64 kvm_call_hyp(void *hypfn, ...); | ||
50 | * | ||
51 | * This is not really a variadic function in the classic C-way and care must | ||
52 | * be taken when calling this to ensure parameters are passed in registers | ||
53 | * only, since the stack will change between the caller and the callee. | ||
54 | * | ||
55 | * Call the function with the first argument containing a pointer to the | ||
56 | * function you wish to call in Hyp mode, and subsequent arguments will be | ||
57 | * passed as r0, r1, and r2 (a maximum of 3 arguments in addition to the | ||
58 | * function pointer can be passed). The function being called must be mapped | ||
59 | * in Hyp mode (see init_hyp_mode in arch/arm/kvm/arm.c). Return values are | ||
60 | * passed in r0 and r1. | ||
61 | * | ||
62 | * The calling convention follows the standard AAPCS: | ||
63 | * r0 - r3: caller save | ||
64 | * r12: caller save | ||
65 | * rest: callee save | ||
66 | */ | ||
67 | ENTRY(kvm_call_hyp) | ||
68 | hvc #0 | ||
69 | bx lr | ||
70 | |||
71 | /******************************************************************** | ||
72 | * Hypervisor exception vector and handlers | ||
73 | */ | ||
74 | |||
75 | .align 5 | ||
76 | __kvm_hyp_vector: | ||
77 | .globl __kvm_hyp_vector | ||
78 | nop | ||
79 | |||
80 | __kvm_hyp_code_end: | ||
81 | .globl __kvm_hyp_code_end | ||
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 10ed4643269f..4decdb618019 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c | |||
@@ -15,3 +15,251 @@ | |||
15 | * along with this program; if not, write to the Free Software | 15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | */ | 17 | */ |
18 | |||
19 | #include <linux/mman.h> | ||
20 | #include <linux/kvm_host.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <asm/idmap.h> | ||
23 | #include <asm/pgalloc.h> | ||
24 | #include <asm/kvm_arm.h> | ||
25 | #include <asm/kvm_mmu.h> | ||
26 | #include <asm/mach/map.h> | ||
27 | |||
28 | extern char __hyp_idmap_text_start[], __hyp_idmap_text_end[]; | ||
29 | |||
30 | static DEFINE_MUTEX(kvm_hyp_pgd_mutex); | ||
31 | |||
32 | static void kvm_set_pte(pte_t *pte, pte_t new_pte) | ||
33 | { | ||
34 | pte_val(*pte) = new_pte; | ||
35 | /* | ||
36 | * flush_pmd_entry just takes a void pointer and cleans the necessary | ||
37 | * cache entries, so we can reuse the function for ptes. | ||
38 | */ | ||
39 | flush_pmd_entry(pte); | ||
40 | } | ||
41 | |||
42 | static void free_ptes(pmd_t *pmd, unsigned long addr) | ||
43 | { | ||
44 | pte_t *pte; | ||
45 | unsigned int i; | ||
46 | |||
47 | for (i = 0; i < PTRS_PER_PMD; i++, addr += PMD_SIZE) { | ||
48 | if (!pmd_none(*pmd) && pmd_table(*pmd)) { | ||
49 | pte = pte_offset_kernel(pmd, addr); | ||
50 | pte_free_kernel(NULL, pte); | ||
51 | } | ||
52 | pmd++; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * free_hyp_pmds - free a Hyp-mode level-2 tables and child level-3 tables | ||
58 | * | ||
59 | * Assumes this is a page table used strictly in Hyp-mode and therefore contains | ||
60 | * only mappings in the kernel memory area, which is above PAGE_OFFSET. | ||
61 | */ | ||
62 | void free_hyp_pmds(void) | ||
63 | { | ||
64 | pgd_t *pgd; | ||
65 | pud_t *pud; | ||
66 | pmd_t *pmd; | ||
67 | unsigned long addr; | ||
68 | |||
69 | mutex_lock(&kvm_hyp_pgd_mutex); | ||
70 | for (addr = PAGE_OFFSET; addr != 0; addr += PGDIR_SIZE) { | ||
71 | pgd = hyp_pgd + pgd_index(addr); | ||
72 | pud = pud_offset(pgd, addr); | ||
73 | |||
74 | if (pud_none(*pud)) | ||
75 | continue; | ||
76 | BUG_ON(pud_bad(*pud)); | ||
77 | |||
78 | pmd = pmd_offset(pud, addr); | ||
79 | free_ptes(pmd, addr); | ||
80 | pmd_free(NULL, pmd); | ||
81 | pud_clear(pud); | ||
82 | } | ||
83 | mutex_unlock(&kvm_hyp_pgd_mutex); | ||
84 | } | ||
85 | |||
86 | static void create_hyp_pte_mappings(pmd_t *pmd, unsigned long start, | ||
87 | unsigned long end) | ||
88 | { | ||
89 | pte_t *pte; | ||
90 | unsigned long addr; | ||
91 | struct page *page; | ||
92 | |||
93 | for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE) { | ||
94 | pte = pte_offset_kernel(pmd, addr); | ||
95 | BUG_ON(!virt_addr_valid(addr)); | ||
96 | page = virt_to_page(addr); | ||
97 | kvm_set_pte(pte, mk_pte(page, PAGE_HYP)); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | static void create_hyp_io_pte_mappings(pmd_t *pmd, unsigned long start, | ||
102 | unsigned long end, | ||
103 | unsigned long *pfn_base) | ||
104 | { | ||
105 | pte_t *pte; | ||
106 | unsigned long addr; | ||
107 | |||
108 | for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE) { | ||
109 | pte = pte_offset_kernel(pmd, addr); | ||
110 | BUG_ON(pfn_valid(*pfn_base)); | ||
111 | kvm_set_pte(pte, pfn_pte(*pfn_base, PAGE_HYP_DEVICE)); | ||
112 | (*pfn_base)++; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | static int create_hyp_pmd_mappings(pud_t *pud, unsigned long start, | ||
117 | unsigned long end, unsigned long *pfn_base) | ||
118 | { | ||
119 | pmd_t *pmd; | ||
120 | pte_t *pte; | ||
121 | unsigned long addr, next; | ||
122 | |||
123 | for (addr = start; addr < end; addr = next) { | ||
124 | pmd = pmd_offset(pud, addr); | ||
125 | |||
126 | BUG_ON(pmd_sect(*pmd)); | ||
127 | |||
128 | if (pmd_none(*pmd)) { | ||
129 | pte = pte_alloc_one_kernel(NULL, addr); | ||
130 | if (!pte) { | ||
131 | kvm_err("Cannot allocate Hyp pte\n"); | ||
132 | return -ENOMEM; | ||
133 | } | ||
134 | pmd_populate_kernel(NULL, pmd, pte); | ||
135 | } | ||
136 | |||
137 | next = pmd_addr_end(addr, end); | ||
138 | |||
139 | /* | ||
140 | * If pfn_base is NULL, we map kernel pages into HYP with the | ||
141 | * virtual address. Otherwise, this is considered an I/O | ||
142 | * mapping and we map the physical region starting at | ||
143 | * *pfn_base to [start, end[. | ||
144 | */ | ||
145 | if (!pfn_base) | ||
146 | create_hyp_pte_mappings(pmd, addr, next); | ||
147 | else | ||
148 | create_hyp_io_pte_mappings(pmd, addr, next, pfn_base); | ||
149 | } | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int __create_hyp_mappings(void *from, void *to, unsigned long *pfn_base) | ||
155 | { | ||
156 | unsigned long start = (unsigned long)from; | ||
157 | unsigned long end = (unsigned long)to; | ||
158 | pgd_t *pgd; | ||
159 | pud_t *pud; | ||
160 | pmd_t *pmd; | ||
161 | unsigned long addr, next; | ||
162 | int err = 0; | ||
163 | |||
164 | BUG_ON(start > end); | ||
165 | if (start < PAGE_OFFSET) | ||
166 | return -EINVAL; | ||
167 | |||
168 | mutex_lock(&kvm_hyp_pgd_mutex); | ||
169 | for (addr = start; addr < end; addr = next) { | ||
170 | pgd = hyp_pgd + pgd_index(addr); | ||
171 | pud = pud_offset(pgd, addr); | ||
172 | |||
173 | if (pud_none_or_clear_bad(pud)) { | ||
174 | pmd = pmd_alloc_one(NULL, addr); | ||
175 | if (!pmd) { | ||
176 | kvm_err("Cannot allocate Hyp pmd\n"); | ||
177 | err = -ENOMEM; | ||
178 | goto out; | ||
179 | } | ||
180 | pud_populate(NULL, pud, pmd); | ||
181 | } | ||
182 | |||
183 | next = pgd_addr_end(addr, end); | ||
184 | err = create_hyp_pmd_mappings(pud, addr, next, pfn_base); | ||
185 | if (err) | ||
186 | goto out; | ||
187 | } | ||
188 | out: | ||
189 | mutex_unlock(&kvm_hyp_pgd_mutex); | ||
190 | return err; | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * create_hyp_mappings - map a kernel virtual address range in Hyp mode | ||
195 | * @from: The virtual kernel start address of the range | ||
196 | * @to: The virtual kernel end address of the range (exclusive) | ||
197 | * | ||
198 | * The same virtual address as the kernel virtual address is also used in | ||
199 | * Hyp-mode mapping to the same underlying physical pages. | ||
200 | * | ||
201 | * Note: Wrapping around zero in the "to" address is not supported. | ||
202 | */ | ||
203 | int create_hyp_mappings(void *from, void *to) | ||
204 | { | ||
205 | return __create_hyp_mappings(from, to, NULL); | ||
206 | } | ||
207 | |||
208 | /** | ||
209 | * create_hyp_io_mappings - map a physical IO range in Hyp mode | ||
210 | * @from: The virtual HYP start address of the range | ||
211 | * @to: The virtual HYP end address of the range (exclusive) | ||
212 | * @addr: The physical start address which gets mapped | ||
213 | */ | ||
214 | int create_hyp_io_mappings(void *from, void *to, phys_addr_t addr) | ||
215 | { | ||
216 | unsigned long pfn = __phys_to_pfn(addr); | ||
217 | return __create_hyp_mappings(from, to, &pfn); | ||
218 | } | ||
219 | |||
220 | int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
221 | { | ||
222 | return -EINVAL; | ||
223 | } | ||
224 | |||
225 | phys_addr_t kvm_mmu_get_httbr(void) | ||
226 | { | ||
227 | VM_BUG_ON(!virt_addr_valid(hyp_pgd)); | ||
228 | return virt_to_phys(hyp_pgd); | ||
229 | } | ||
230 | |||
231 | int kvm_mmu_init(void) | ||
232 | { | ||
233 | return hyp_pgd ? 0 : -ENOMEM; | ||
234 | } | ||
235 | |||
236 | /** | ||
237 | * kvm_clear_idmap - remove all idmaps from the hyp pgd | ||
238 | * | ||
239 | * Free the underlying pmds for all pgds in range and clear the pgds (but | ||
240 | * don't free them) afterwards. | ||
241 | */ | ||
242 | void kvm_clear_hyp_idmap(void) | ||
243 | { | ||
244 | unsigned long addr, end; | ||
245 | unsigned long next; | ||
246 | pgd_t *pgd = hyp_pgd; | ||
247 | pud_t *pud; | ||
248 | pmd_t *pmd; | ||
249 | |||
250 | addr = virt_to_phys(__hyp_idmap_text_start); | ||
251 | end = virt_to_phys(__hyp_idmap_text_end); | ||
252 | |||
253 | pgd += pgd_index(addr); | ||
254 | do { | ||
255 | next = pgd_addr_end(addr, end); | ||
256 | if (pgd_none_or_clear_bad(pgd)) | ||
257 | continue; | ||
258 | pud = pud_offset(pgd, addr); | ||
259 | pmd = pmd_offset(pud, addr); | ||
260 | |||
261 | pud_clear(pud); | ||
262 | clean_pmd_entry(pmd); | ||
263 | pmd_free(NULL, (pmd_t *)((unsigned long)pmd & PAGE_MASK)); | ||
264 | } while (pgd++, addr = next, addr < end); | ||
265 | } | ||