]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/arm/kvm/handle_exit.c
ARM: KVM: move kvm_target_cpu to guest.c
[karo-tx-linux.git] / arch / arm / kvm / handle_exit.c
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 #include "trace.h"
27
28 typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *);
29
30 static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
31 {
32         /* SVC called from Hyp mode should never get here */
33         kvm_debug("SVC called from Hyp mode shouldn't go here\n");
34         BUG();
35         return -EINVAL; /* Squash warning */
36 }
37
38 static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
39 {
40         trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0),
41                       kvm_vcpu_hvc_get_imm(vcpu));
42
43         if (kvm_psci_call(vcpu))
44                 return 1;
45
46         kvm_inject_undefined(vcpu);
47         return 1;
48 }
49
50 static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
51 {
52         if (kvm_psci_call(vcpu))
53                 return 1;
54
55         kvm_inject_undefined(vcpu);
56         return 1;
57 }
58
59 static int handle_pabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
60 {
61         /* The hypervisor should never cause aborts */
62         kvm_err("Prefetch Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n",
63                 kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu));
64         return -EFAULT;
65 }
66
67 static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run)
68 {
69         /* This is either an error in the ws. code or an external abort */
70         kvm_err("Data Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n",
71                 kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu));
72         return -EFAULT;
73 }
74
75 static exit_handle_fn arm_exit_handlers[] = {
76         [HSR_EC_WFI]            = kvm_handle_wfi,
77         [HSR_EC_CP15_32]        = kvm_handle_cp15_32,
78         [HSR_EC_CP15_64]        = kvm_handle_cp15_64,
79         [HSR_EC_CP14_MR]        = kvm_handle_cp14_access,
80         [HSR_EC_CP14_LS]        = kvm_handle_cp14_load_store,
81         [HSR_EC_CP14_64]        = kvm_handle_cp14_access,
82         [HSR_EC_CP_0_13]        = kvm_handle_cp_0_13_access,
83         [HSR_EC_CP10_ID]        = kvm_handle_cp10_id,
84         [HSR_EC_SVC_HYP]        = handle_svc_hyp,
85         [HSR_EC_HVC]            = handle_hvc,
86         [HSR_EC_SMC]            = handle_smc,
87         [HSR_EC_IABT]           = kvm_handle_guest_abort,
88         [HSR_EC_IABT_HYP]       = handle_pabt_hyp,
89         [HSR_EC_DABT]           = kvm_handle_guest_abort,
90         [HSR_EC_DABT_HYP]       = handle_dabt_hyp,
91 };
92
93 static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu)
94 {
95         u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
96
97         if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) ||
98             !arm_exit_handlers[hsr_ec]) {
99                 kvm_err("Unkown exception class: hsr: %#08x\n",
100                         (unsigned int)kvm_vcpu_get_hsr(vcpu));
101                 BUG();
102         }
103
104         return arm_exit_handlers[hsr_ec];
105 }
106
107 /*
108  * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
109  * proper exit to userspace.
110  */
111 int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
112                        int exception_index)
113 {
114         exit_handle_fn exit_handler;
115
116         switch (exception_index) {
117         case ARM_EXCEPTION_IRQ:
118                 return 1;
119         case ARM_EXCEPTION_UNDEFINED:
120                 kvm_err("Undefined exception in Hyp mode at: %#08lx\n",
121                         kvm_vcpu_get_hyp_pc(vcpu));
122                 BUG();
123                 panic("KVM: Hypervisor undefined exception!\n");
124         case ARM_EXCEPTION_DATA_ABORT:
125         case ARM_EXCEPTION_PREF_ABORT:
126         case ARM_EXCEPTION_HVC:
127                 /*
128                  * See ARM ARM B1.14.1: "Hyp traps on instructions
129                  * that fail their condition code check"
130                  */
131                 if (!kvm_condition_valid(vcpu)) {
132                         kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
133                         return 1;
134                 }
135
136                 exit_handler = kvm_get_exit_handler(vcpu);
137
138                 return exit_handler(vcpu, run);
139         default:
140                 kvm_pr_unimpl("Unsupported exception type: %d",
141                               exception_index);
142                 run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
143                 return 0;
144         }
145 }