diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/include/asm/kvm_arm.h | 124 | ||||
-rw-r--r-- | arch/arm/include/asm/kvm_asm.h | 20 | ||||
-rw-r--r-- | arch/arm/include/asm/kvm_host.h | 1 | ||||
-rw-r--r-- | arch/arm/include/asm/kvm_mmu.h | 29 | ||||
-rw-r--r-- | arch/arm/include/asm/pgtable-3level-hwdef.h | 4 | ||||
-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 |
9 files changed, 755 insertions, 0 deletions
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index dc678e193417..8875b3f605a7 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h | |||
@@ -21,4 +21,128 @@ | |||
21 | 21 | ||
22 | #include <linux/types.h> | 22 | #include <linux/types.h> |
23 | 23 | ||
24 | /* Hyp Configuration Register (HCR) bits */ | ||
25 | #define HCR_TGE (1 << 27) | ||
26 | #define HCR_TVM (1 << 26) | ||
27 | #define HCR_TTLB (1 << 25) | ||
28 | #define HCR_TPU (1 << 24) | ||
29 | #define HCR_TPC (1 << 23) | ||
30 | #define HCR_TSW (1 << 22) | ||
31 | #define HCR_TAC (1 << 21) | ||
32 | #define HCR_TIDCP (1 << 20) | ||
33 | #define HCR_TSC (1 << 19) | ||
34 | #define HCR_TID3 (1 << 18) | ||
35 | #define HCR_TID2 (1 << 17) | ||
36 | #define HCR_TID1 (1 << 16) | ||
37 | #define HCR_TID0 (1 << 15) | ||
38 | #define HCR_TWE (1 << 14) | ||
39 | #define HCR_TWI (1 << 13) | ||
40 | #define HCR_DC (1 << 12) | ||
41 | #define HCR_BSU (3 << 10) | ||
42 | #define HCR_BSU_IS (1 << 10) | ||
43 | #define HCR_FB (1 << 9) | ||
44 | #define HCR_VA (1 << 8) | ||
45 | #define HCR_VI (1 << 7) | ||
46 | #define HCR_VF (1 << 6) | ||
47 | #define HCR_AMO (1 << 5) | ||
48 | #define HCR_IMO (1 << 4) | ||
49 | #define HCR_FMO (1 << 3) | ||
50 | #define HCR_PTW (1 << 2) | ||
51 | #define HCR_SWIO (1 << 1) | ||
52 | #define HCR_VM 1 | ||
53 | |||
54 | /* | ||
55 | * The bits we set in HCR: | ||
56 | * TAC: Trap ACTLR | ||
57 | * TSC: Trap SMC | ||
58 | * TSW: Trap cache operations by set/way | ||
59 | * TWI: Trap WFI | ||
60 | * TIDCP: Trap L2CTLR/L2ECTLR | ||
61 | * BSU_IS: Upgrade barriers to the inner shareable domain | ||
62 | * FB: Force broadcast of all maintainance operations | ||
63 | * AMO: Override CPSR.A and enable signaling with VA | ||
64 | * IMO: Override CPSR.I and enable signaling with VI | ||
65 | * FMO: Override CPSR.F and enable signaling with VF | ||
66 | * SWIO: Turn set/way invalidates into set/way clean+invalidate | ||
67 | */ | ||
68 | #define HCR_GUEST_MASK (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \ | ||
69 | HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \ | ||
70 | HCR_SWIO | HCR_TIDCP) | ||
71 | |||
72 | /* Hyp System Control Register (HSCTLR) bits */ | ||
73 | #define HSCTLR_TE (1 << 30) | ||
74 | #define HSCTLR_EE (1 << 25) | ||
75 | #define HSCTLR_FI (1 << 21) | ||
76 | #define HSCTLR_WXN (1 << 19) | ||
77 | #define HSCTLR_I (1 << 12) | ||
78 | #define HSCTLR_C (1 << 2) | ||
79 | #define HSCTLR_A (1 << 1) | ||
80 | #define HSCTLR_M 1 | ||
81 | #define HSCTLR_MASK (HSCTLR_M | HSCTLR_A | HSCTLR_C | HSCTLR_I | \ | ||
82 | HSCTLR_WXN | HSCTLR_FI | HSCTLR_EE | HSCTLR_TE) | ||
83 | |||
84 | /* TTBCR and HTCR Registers bits */ | ||
85 | #define TTBCR_EAE (1 << 31) | ||
86 | #define TTBCR_IMP (1 << 30) | ||
87 | #define TTBCR_SH1 (3 << 28) | ||
88 | #define TTBCR_ORGN1 (3 << 26) | ||
89 | #define TTBCR_IRGN1 (3 << 24) | ||
90 | #define TTBCR_EPD1 (1 << 23) | ||
91 | #define TTBCR_A1 (1 << 22) | ||
92 | #define TTBCR_T1SZ (3 << 16) | ||
93 | #define TTBCR_SH0 (3 << 12) | ||
94 | #define TTBCR_ORGN0 (3 << 10) | ||
95 | #define TTBCR_IRGN0 (3 << 8) | ||
96 | #define TTBCR_EPD0 (1 << 7) | ||
97 | #define TTBCR_T0SZ 3 | ||
98 | #define HTCR_MASK (TTBCR_T0SZ | TTBCR_IRGN0 | TTBCR_ORGN0 | TTBCR_SH0) | ||
99 | |||
100 | /* Hyp Debug Configuration Register bits */ | ||
101 | #define HDCR_TDRA (1 << 11) | ||
102 | #define HDCR_TDOSA (1 << 10) | ||
103 | #define HDCR_TDA (1 << 9) | ||
104 | #define HDCR_TDE (1 << 8) | ||
105 | #define HDCR_HPME (1 << 7) | ||
106 | #define HDCR_TPM (1 << 6) | ||
107 | #define HDCR_TPMCR (1 << 5) | ||
108 | #define HDCR_HPMN_MASK (0x1F) | ||
109 | |||
110 | /* | ||
111 | * The architecture supports 40-bit IPA as input to the 2nd stage translations | ||
112 | * and PTRS_PER_S2_PGD becomes 1024, because each entry covers 1GB of address | ||
113 | * space. | ||
114 | */ | ||
115 | #define KVM_PHYS_SHIFT (40) | ||
116 | #define KVM_PHYS_SIZE (1ULL << KVM_PHYS_SHIFT) | ||
117 | #define KVM_PHYS_MASK (KVM_PHYS_SIZE - 1ULL) | ||
118 | #define PTRS_PER_S2_PGD (1ULL << (KVM_PHYS_SHIFT - 30)) | ||
119 | #define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t)) | ||
120 | #define S2_PGD_SIZE (1 << S2_PGD_ORDER) | ||
121 | |||
122 | /* Virtualization Translation Control Register (VTCR) bits */ | ||
123 | #define VTCR_SH0 (3 << 12) | ||
124 | #define VTCR_ORGN0 (3 << 10) | ||
125 | #define VTCR_IRGN0 (3 << 8) | ||
126 | #define VTCR_SL0 (3 << 6) | ||
127 | #define VTCR_S (1 << 4) | ||
128 | #define VTCR_T0SZ (0xf) | ||
129 | #define VTCR_MASK (VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0 | VTCR_SL0 | \ | ||
130 | VTCR_S | VTCR_T0SZ) | ||
131 | #define VTCR_HTCR_SH (VTCR_SH0 | VTCR_ORGN0 | VTCR_IRGN0) | ||
132 | #define VTCR_SL_L2 (0 << 6) /* Starting-level: 2 */ | ||
133 | #define VTCR_SL_L1 (1 << 6) /* Starting-level: 1 */ | ||
134 | #define KVM_VTCR_SL0 VTCR_SL_L1 | ||
135 | /* stage-2 input address range defined as 2^(32-T0SZ) */ | ||
136 | #define KVM_T0SZ (32 - KVM_PHYS_SHIFT) | ||
137 | #define KVM_VTCR_T0SZ (KVM_T0SZ & VTCR_T0SZ) | ||
138 | #define KVM_VTCR_S ((KVM_VTCR_T0SZ << 1) & VTCR_S) | ||
139 | |||
140 | /* Virtualization Translation Table Base Register (VTTBR) bits */ | ||
141 | #if KVM_VTCR_SL0 == VTCR_SL_L2 /* see ARM DDI 0406C: B4-1720 */ | ||
142 | #define VTTBR_X (14 - KVM_T0SZ) | ||
143 | #else | ||
144 | #define VTTBR_X (5 - KVM_T0SZ) | ||
145 | #endif | ||
146 | |||
147 | |||
24 | #endif /* __ARM_KVM_ARM_H__ */ | 148 | #endif /* __ARM_KVM_ARM_H__ */ |
diff --git a/arch/arm/include/asm/kvm_asm.h b/arch/arm/include/asm/kvm_asm.h index f9993e5fb695..81324e2eb3f9 100644 --- a/arch/arm/include/asm/kvm_asm.h +++ b/arch/arm/include/asm/kvm_asm.h | |||
@@ -54,5 +54,25 @@ | |||
54 | #define ARM_EXCEPTION_DATA_ABORT 4 | 54 | #define ARM_EXCEPTION_DATA_ABORT 4 |
55 | #define ARM_EXCEPTION_IRQ 5 | 55 | #define ARM_EXCEPTION_IRQ 5 |
56 | #define ARM_EXCEPTION_FIQ 6 | 56 | #define ARM_EXCEPTION_FIQ 6 |
57 | #define ARM_EXCEPTION_HVC 7 | ||
58 | |||
59 | #ifndef __ASSEMBLY__ | ||
60 | struct kvm_vcpu; | ||
61 | |||
62 | extern char __kvm_hyp_init[]; | ||
63 | extern char __kvm_hyp_init_end[]; | ||
64 | |||
65 | extern char __kvm_hyp_exit[]; | ||
66 | extern char __kvm_hyp_exit_end[]; | ||
67 | |||
68 | extern char __kvm_hyp_vector[]; | ||
69 | |||
70 | extern char __kvm_hyp_code_start[]; | ||
71 | extern char __kvm_hyp_code_end[]; | ||
72 | |||
73 | extern void __kvm_flush_vm_context(void); | ||
74 | |||
75 | extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu); | ||
76 | #endif | ||
57 | 77 | ||
58 | #endif /* __ARM_KVM_ASM_H__ */ | 78 | #endif /* __ARM_KVM_ASM_H__ */ |
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 0d9938a20751..067ef2898c26 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h | |||
@@ -111,4 +111,5 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); | |||
111 | struct kvm_one_reg; | 111 | struct kvm_one_reg; |
112 | int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); | 112 | int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); |
113 | int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); | 113 | int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); |
114 | u64 kvm_call_hyp(void *hypfn, ...); | ||
114 | #endif /* __ARM_KVM_HOST_H__ */ | 115 | #endif /* __ARM_KVM_HOST_H__ */ |
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h new file mode 100644 index 000000000000..e8679b317b0f --- /dev/null +++ b/arch/arm/include/asm/kvm_mmu.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 - Virtual Open Systems and Columbia University | ||
3 | * Author: Christoffer Dall <c.dall@virtualopensystems.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License, version 2, as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | ||
18 | |||
19 | #ifndef __ARM_KVM_MMU_H__ | ||
20 | #define __ARM_KVM_MMU_H__ | ||
21 | |||
22 | int create_hyp_mappings(void *from, void *to); | ||
23 | int create_hyp_io_mappings(void *from, void *to, phys_addr_t); | ||
24 | void free_hyp_pmds(void); | ||
25 | |||
26 | phys_addr_t kvm_mmu_get_httbr(void); | ||
27 | int kvm_mmu_init(void); | ||
28 | void kvm_clear_hyp_idmap(void); | ||
29 | #endif /* __ARM_KVM_MMU_H__ */ | ||
diff --git a/arch/arm/include/asm/pgtable-3level-hwdef.h b/arch/arm/include/asm/pgtable-3level-hwdef.h index a2d404ed1ade..18f5cef82ad5 100644 --- a/arch/arm/include/asm/pgtable-3level-hwdef.h +++ b/arch/arm/include/asm/pgtable-3level-hwdef.h | |||
@@ -32,6 +32,9 @@ | |||
32 | #define PMD_TYPE_SECT (_AT(pmdval_t, 1) << 0) | 32 | #define PMD_TYPE_SECT (_AT(pmdval_t, 1) << 0) |
33 | #define PMD_BIT4 (_AT(pmdval_t, 0)) | 33 | #define PMD_BIT4 (_AT(pmdval_t, 0)) |
34 | #define PMD_DOMAIN(x) (_AT(pmdval_t, 0)) | 34 | #define PMD_DOMAIN(x) (_AT(pmdval_t, 0)) |
35 | #define PMD_APTABLE_SHIFT (61) | ||
36 | #define PMD_APTABLE (_AT(pgdval_t, 3) << PGD_APTABLE_SHIFT) | ||
37 | #define PMD_PXNTABLE (_AT(pgdval_t, 1) << 59) | ||
35 | 38 | ||
36 | /* | 39 | /* |
37 | * - section | 40 | * - section |
@@ -41,6 +44,7 @@ | |||
41 | #define PMD_SECT_S (_AT(pmdval_t, 3) << 8) | 44 | #define PMD_SECT_S (_AT(pmdval_t, 3) << 8) |
42 | #define PMD_SECT_AF (_AT(pmdval_t, 1) << 10) | 45 | #define PMD_SECT_AF (_AT(pmdval_t, 1) << 10) |
43 | #define PMD_SECT_nG (_AT(pmdval_t, 1) << 11) | 46 | #define PMD_SECT_nG (_AT(pmdval_t, 1) << 11) |
47 | #define PMD_SECT_PXN (_AT(pmdval_t, 1) << 53) | ||
44 | #define PMD_SECT_XN (_AT(pmdval_t, 1) << 54) | 48 | #define PMD_SECT_XN (_AT(pmdval_t, 1) << 54) |
45 | #define PMD_SECT_AP_WRITE (_AT(pmdval_t, 0)) | 49 | #define PMD_SECT_AP_WRITE (_AT(pmdval_t, 0)) |
46 | #define PMD_SECT_AP_READ (_AT(pmdval_t, 0)) | 50 | #define PMD_SECT_AP_READ (_AT(pmdval_t, 0)) |
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 | } | ||