diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/include/asm/kvm_host.h | 3 | ||||
-rw-r--r-- | arch/arm/kvm/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/kvm/arm.c | 113 | ||||
-rw-r--r-- | arch/arm/kvm/handle_exit.c | 140 |
4 files changed, 144 insertions, 114 deletions
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index eb836e6d3c59..24f457aeba4d 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h | |||
@@ -183,4 +183,7 @@ struct kvm_one_reg; | |||
183 | int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); | 183 | int kvm_arm_coproc_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); |
184 | int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); | 184 | int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); |
185 | 185 | ||
186 | int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, | ||
187 | int exception_index); | ||
188 | |||
186 | #endif /* __ARM_KVM_HOST_H__ */ | 189 | #endif /* __ARM_KVM_HOST_H__ */ |
diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile index fc96ce6f2357..8dc5e76cb789 100644 --- a/arch/arm/kvm/Makefile +++ b/arch/arm/kvm/Makefile | |||
@@ -17,7 +17,7 @@ AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt) | |||
17 | kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o) | 17 | kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o) |
18 | 18 | ||
19 | obj-y += kvm-arm.o init.o interrupts.o | 19 | obj-y += kvm-arm.o init.o interrupts.o |
20 | obj-y += arm.o guest.o mmu.o emulate.o reset.o | 20 | obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o |
21 | obj-y += coproc.o coproc_a15.o mmio.o psci.o | 21 | obj-y += coproc.o coproc_a15.o mmio.o psci.o |
22 | obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o | 22 | obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o |
23 | obj-$(CONFIG_KVM_ARM_TIMER) += arch_timer.o | 23 | obj-$(CONFIG_KVM_ARM_TIMER) += arch_timer.o |
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 6b776183ff93..de783ee8c341 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #define CREATE_TRACE_POINTS | 30 | #define CREATE_TRACE_POINTS |
31 | #include "trace.h" | 31 | #include "trace.h" |
32 | 32 | ||
33 | #include <asm/unified.h> | ||
34 | #include <asm/uaccess.h> | 33 | #include <asm/uaccess.h> |
35 | #include <asm/ptrace.h> | 34 | #include <asm/ptrace.h> |
36 | #include <asm/mman.h> | 35 | #include <asm/mman.h> |
@@ -480,118 +479,6 @@ static void update_vttbr(struct kvm *kvm) | |||
480 | spin_unlock(&kvm_vmid_lock); | 479 | spin_unlock(&kvm_vmid_lock); |
481 | } | 480 | } |
482 | 481 | ||
483 | static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
484 | { | ||
485 | /* SVC called from Hyp mode should never get here */ | ||
486 | kvm_debug("SVC called from Hyp mode shouldn't go here\n"); | ||
487 | BUG(); | ||
488 | return -EINVAL; /* Squash warning */ | ||
489 | } | ||
490 | |||
491 | static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
492 | { | ||
493 | trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0), | ||
494 | kvm_vcpu_get_hsr(vcpu) & HSR_HVC_IMM_MASK); | ||
495 | |||
496 | if (kvm_psci_call(vcpu)) | ||
497 | return 1; | ||
498 | |||
499 | kvm_inject_undefined(vcpu); | ||
500 | return 1; | ||
501 | } | ||
502 | |||
503 | static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
504 | { | ||
505 | if (kvm_psci_call(vcpu)) | ||
506 | return 1; | ||
507 | |||
508 | kvm_inject_undefined(vcpu); | ||
509 | return 1; | ||
510 | } | ||
511 | |||
512 | static int handle_pabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
513 | { | ||
514 | /* The hypervisor should never cause aborts */ | ||
515 | kvm_err("Prefetch Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n", | ||
516 | kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu)); | ||
517 | return -EFAULT; | ||
518 | } | ||
519 | |||
520 | static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
521 | { | ||
522 | /* This is either an error in the ws. code or an external abort */ | ||
523 | kvm_err("Data Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n", | ||
524 | kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu)); | ||
525 | return -EFAULT; | ||
526 | } | ||
527 | |||
528 | typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *); | ||
529 | static exit_handle_fn arm_exit_handlers[] = { | ||
530 | [HSR_EC_WFI] = kvm_handle_wfi, | ||
531 | [HSR_EC_CP15_32] = kvm_handle_cp15_32, | ||
532 | [HSR_EC_CP15_64] = kvm_handle_cp15_64, | ||
533 | [HSR_EC_CP14_MR] = kvm_handle_cp14_access, | ||
534 | [HSR_EC_CP14_LS] = kvm_handle_cp14_load_store, | ||
535 | [HSR_EC_CP14_64] = kvm_handle_cp14_access, | ||
536 | [HSR_EC_CP_0_13] = kvm_handle_cp_0_13_access, | ||
537 | [HSR_EC_CP10_ID] = kvm_handle_cp10_id, | ||
538 | [HSR_EC_SVC_HYP] = handle_svc_hyp, | ||
539 | [HSR_EC_HVC] = handle_hvc, | ||
540 | [HSR_EC_SMC] = handle_smc, | ||
541 | [HSR_EC_IABT] = kvm_handle_guest_abort, | ||
542 | [HSR_EC_IABT_HYP] = handle_pabt_hyp, | ||
543 | [HSR_EC_DABT] = kvm_handle_guest_abort, | ||
544 | [HSR_EC_DABT_HYP] = handle_dabt_hyp, | ||
545 | }; | ||
546 | |||
547 | /* | ||
548 | * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on | ||
549 | * proper exit to QEMU. | ||
550 | */ | ||
551 | static int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, | ||
552 | int exception_index) | ||
553 | { | ||
554 | unsigned long hsr_ec; | ||
555 | |||
556 | switch (exception_index) { | ||
557 | case ARM_EXCEPTION_IRQ: | ||
558 | return 1; | ||
559 | case ARM_EXCEPTION_UNDEFINED: | ||
560 | kvm_err("Undefined exception in Hyp mode at: %#08lx\n", | ||
561 | kvm_vcpu_get_hyp_pc(vcpu)); | ||
562 | BUG(); | ||
563 | panic("KVM: Hypervisor undefined exception!\n"); | ||
564 | case ARM_EXCEPTION_DATA_ABORT: | ||
565 | case ARM_EXCEPTION_PREF_ABORT: | ||
566 | case ARM_EXCEPTION_HVC: | ||
567 | hsr_ec = kvm_vcpu_trap_get_class(vcpu); | ||
568 | |||
569 | if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) | ||
570 | || !arm_exit_handlers[hsr_ec]) { | ||
571 | kvm_err("Unkown exception class: %#08lx, " | ||
572 | "hsr: %#08x\n", hsr_ec, | ||
573 | (unsigned int)kvm_vcpu_get_hsr(vcpu)); | ||
574 | BUG(); | ||
575 | } | ||
576 | |||
577 | /* | ||
578 | * See ARM ARM B1.14.1: "Hyp traps on instructions | ||
579 | * that fail their condition code check" | ||
580 | */ | ||
581 | if (!kvm_condition_valid(vcpu)) { | ||
582 | kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); | ||
583 | return 1; | ||
584 | } | ||
585 | |||
586 | return arm_exit_handlers[hsr_ec](vcpu, run); | ||
587 | default: | ||
588 | kvm_pr_unimpl("Unsupported exception type: %d", | ||
589 | exception_index); | ||
590 | run->exit_reason = KVM_EXIT_INTERNAL_ERROR; | ||
591 | return 0; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) | 482 | static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) |
596 | { | 483 | { |
597 | if (likely(vcpu->arch.has_run_once)) | 484 | if (likely(vcpu->arch.has_run_once)) |
diff --git a/arch/arm/kvm/handle_exit.c b/arch/arm/kvm/handle_exit.c new file mode 100644 index 000000000000..f1cc3a8a9c7e --- /dev/null +++ b/arch/arm/kvm/handle_exit.c | |||
@@ -0,0 +1,140 @@ | |||
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 | #include <linux/kvm.h> | ||
20 | #include <linux/kvm_host.h> | ||
21 | #include <asm/kvm_emulate.h> | ||
22 | #include <asm/kvm_coproc.h> | ||
23 | #include <asm/kvm_mmu.h> | ||
24 | #include <asm/kvm_psci.h> | ||
25 | |||
26 | typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *); | ||
27 | |||
28 | static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
29 | { | ||
30 | /* SVC called from Hyp mode should never get here */ | ||
31 | kvm_debug("SVC called from Hyp mode shouldn't go here\n"); | ||
32 | BUG(); | ||
33 | return -EINVAL; /* Squash warning */ | ||
34 | } | ||
35 | |||
36 | static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
37 | { | ||
38 | if (kvm_psci_call(vcpu)) | ||
39 | return 1; | ||
40 | |||
41 | kvm_inject_undefined(vcpu); | ||
42 | return 1; | ||
43 | } | ||
44 | |||
45 | static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
46 | { | ||
47 | if (kvm_psci_call(vcpu)) | ||
48 | return 1; | ||
49 | |||
50 | kvm_inject_undefined(vcpu); | ||
51 | return 1; | ||
52 | } | ||
53 | |||
54 | static int handle_pabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
55 | { | ||
56 | /* The hypervisor should never cause aborts */ | ||
57 | kvm_err("Prefetch Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n", | ||
58 | kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu)); | ||
59 | return -EFAULT; | ||
60 | } | ||
61 | |||
62 | static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) | ||
63 | { | ||
64 | /* This is either an error in the ws. code or an external abort */ | ||
65 | kvm_err("Data Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n", | ||
66 | kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu)); | ||
67 | return -EFAULT; | ||
68 | } | ||
69 | |||
70 | static exit_handle_fn arm_exit_handlers[] = { | ||
71 | [HSR_EC_WFI] = kvm_handle_wfi, | ||
72 | [HSR_EC_CP15_32] = kvm_handle_cp15_32, | ||
73 | [HSR_EC_CP15_64] = kvm_handle_cp15_64, | ||
74 | [HSR_EC_CP14_MR] = kvm_handle_cp14_access, | ||
75 | [HSR_EC_CP14_LS] = kvm_handle_cp14_load_store, | ||
76 | [HSR_EC_CP14_64] = kvm_handle_cp14_access, | ||
77 | [HSR_EC_CP_0_13] = kvm_handle_cp_0_13_access, | ||
78 | [HSR_EC_CP10_ID] = kvm_handle_cp10_id, | ||
79 | [HSR_EC_SVC_HYP] = handle_svc_hyp, | ||
80 | [HSR_EC_HVC] = handle_hvc, | ||
81 | [HSR_EC_SMC] = handle_smc, | ||
82 | [HSR_EC_IABT] = kvm_handle_guest_abort, | ||
83 | [HSR_EC_IABT_HYP] = handle_pabt_hyp, | ||
84 | [HSR_EC_DABT] = kvm_handle_guest_abort, | ||
85 | [HSR_EC_DABT_HYP] = handle_dabt_hyp, | ||
86 | }; | ||
87 | |||
88 | static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu) | ||
89 | { | ||
90 | u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu); | ||
91 | |||
92 | if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) || | ||
93 | !arm_exit_handlers[hsr_ec]) { | ||
94 | kvm_err("Unkown exception class: hsr: %#08x\n", | ||
95 | (unsigned int)kvm_vcpu_get_hsr(vcpu)); | ||
96 | BUG(); | ||
97 | } | ||
98 | |||
99 | return arm_exit_handlers[hsr_ec]; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on | ||
104 | * proper exit to userspace. | ||
105 | */ | ||
106 | int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, | ||
107 | int exception_index) | ||
108 | { | ||
109 | exit_handle_fn exit_handler; | ||
110 | |||
111 | switch (exception_index) { | ||
112 | case ARM_EXCEPTION_IRQ: | ||
113 | return 1; | ||
114 | case ARM_EXCEPTION_UNDEFINED: | ||
115 | kvm_err("Undefined exception in Hyp mode at: %#08lx\n", | ||
116 | kvm_vcpu_get_hyp_pc(vcpu)); | ||
117 | BUG(); | ||
118 | panic("KVM: Hypervisor undefined exception!\n"); | ||
119 | case ARM_EXCEPTION_DATA_ABORT: | ||
120 | case ARM_EXCEPTION_PREF_ABORT: | ||
121 | case ARM_EXCEPTION_HVC: | ||
122 | /* | ||
123 | * See ARM ARM B1.14.1: "Hyp traps on instructions | ||
124 | * that fail their condition code check" | ||
125 | */ | ||
126 | if (!kvm_condition_valid(vcpu)) { | ||
127 | kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); | ||
128 | return 1; | ||
129 | } | ||
130 | |||
131 | exit_handler = kvm_get_exit_handler(vcpu); | ||
132 | |||
133 | return exit_handler(vcpu, run); | ||
134 | default: | ||
135 | kvm_pr_unimpl("Unsupported exception type: %d", | ||
136 | exception_index); | ||
137 | run->exit_reason = KVM_EXIT_INTERNAL_ERROR; | ||
138 | return 0; | ||
139 | } | ||
140 | } | ||