Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.10 164 lines 4.8 kB view raw
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#include <trace/events/kvm.h> 26 27#include "trace.h" 28 29#include "trace.h" 30 31typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *); 32 33static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) 34{ 35 /* SVC called from Hyp mode should never get here */ 36 kvm_debug("SVC called from Hyp mode shouldn't go here\n"); 37 BUG(); 38 return -EINVAL; /* Squash warning */ 39} 40 41static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) 42{ 43 trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0), 44 kvm_vcpu_hvc_get_imm(vcpu)); 45 46 if (kvm_psci_call(vcpu)) 47 return 1; 48 49 kvm_inject_undefined(vcpu); 50 return 1; 51} 52 53static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) 54{ 55 if (kvm_psci_call(vcpu)) 56 return 1; 57 58 kvm_inject_undefined(vcpu); 59 return 1; 60} 61 62static int handle_pabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) 63{ 64 /* The hypervisor should never cause aborts */ 65 kvm_err("Prefetch 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 70static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) 71{ 72 /* This is either an error in the ws. code or an external abort */ 73 kvm_err("Data Abort taken from Hyp mode at %#08lx (HSR: %#08x)\n", 74 kvm_vcpu_get_hfar(vcpu), kvm_vcpu_get_hsr(vcpu)); 75 return -EFAULT; 76} 77 78/** 79 * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest 80 * @vcpu: the vcpu pointer 81 * @run: the kvm_run structure pointer 82 * 83 * Simply sets the wait_for_interrupts flag on the vcpu structure, which will 84 * halt execution of world-switches and schedule other host processes until 85 * there is an incoming IRQ or FIQ to the VM. 86 */ 87static int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) 88{ 89 trace_kvm_wfi(*vcpu_pc(vcpu)); 90 kvm_vcpu_block(vcpu); 91 return 1; 92} 93 94static exit_handle_fn arm_exit_handlers[] = { 95 [HSR_EC_WFI] = kvm_handle_wfi, 96 [HSR_EC_CP15_32] = kvm_handle_cp15_32, 97 [HSR_EC_CP15_64] = kvm_handle_cp15_64, 98 [HSR_EC_CP14_MR] = kvm_handle_cp14_access, 99 [HSR_EC_CP14_LS] = kvm_handle_cp14_load_store, 100 [HSR_EC_CP14_64] = kvm_handle_cp14_access, 101 [HSR_EC_CP_0_13] = kvm_handle_cp_0_13_access, 102 [HSR_EC_CP10_ID] = kvm_handle_cp10_id, 103 [HSR_EC_SVC_HYP] = handle_svc_hyp, 104 [HSR_EC_HVC] = handle_hvc, 105 [HSR_EC_SMC] = handle_smc, 106 [HSR_EC_IABT] = kvm_handle_guest_abort, 107 [HSR_EC_IABT_HYP] = handle_pabt_hyp, 108 [HSR_EC_DABT] = kvm_handle_guest_abort, 109 [HSR_EC_DABT_HYP] = handle_dabt_hyp, 110}; 111 112static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu) 113{ 114 u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu); 115 116 if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) || 117 !arm_exit_handlers[hsr_ec]) { 118 kvm_err("Unknown exception class: hsr: %#08x\n", 119 (unsigned int)kvm_vcpu_get_hsr(vcpu)); 120 BUG(); 121 } 122 123 return arm_exit_handlers[hsr_ec]; 124} 125 126/* 127 * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on 128 * proper exit to userspace. 129 */ 130int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, 131 int exception_index) 132{ 133 exit_handle_fn exit_handler; 134 135 switch (exception_index) { 136 case ARM_EXCEPTION_IRQ: 137 return 1; 138 case ARM_EXCEPTION_UNDEFINED: 139 kvm_err("Undefined exception in Hyp mode at: %#08lx\n", 140 kvm_vcpu_get_hyp_pc(vcpu)); 141 BUG(); 142 panic("KVM: Hypervisor undefined exception!\n"); 143 case ARM_EXCEPTION_DATA_ABORT: 144 case ARM_EXCEPTION_PREF_ABORT: 145 case ARM_EXCEPTION_HVC: 146 /* 147 * See ARM ARM B1.14.1: "Hyp traps on instructions 148 * that fail their condition code check" 149 */ 150 if (!kvm_condition_valid(vcpu)) { 151 kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); 152 return 1; 153 } 154 155 exit_handler = kvm_get_exit_handler(vcpu); 156 157 return exit_handler(vcpu, run); 158 default: 159 kvm_pr_unimpl("Unsupported exception type: %d", 160 exception_index); 161 run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 162 return 0; 163 } 164}