Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.11 161 lines 4.7 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 kvm_inject_undefined(vcpu); 56 return 1; 57} 58 59static 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 67static 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/** 76 * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest 77 * @vcpu: the vcpu pointer 78 * @run: the kvm_run structure pointer 79 * 80 * Simply sets the wait_for_interrupts flag on the vcpu structure, which will 81 * halt execution of world-switches and schedule other host processes until 82 * there is an incoming IRQ or FIQ to the VM. 83 */ 84static int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) 85{ 86 trace_kvm_wfi(*vcpu_pc(vcpu)); 87 kvm_vcpu_block(vcpu); 88 return 1; 89} 90 91static exit_handle_fn arm_exit_handlers[] = { 92 [HSR_EC_WFI] = kvm_handle_wfi, 93 [HSR_EC_CP15_32] = kvm_handle_cp15_32, 94 [HSR_EC_CP15_64] = kvm_handle_cp15_64, 95 [HSR_EC_CP14_MR] = kvm_handle_cp14_access, 96 [HSR_EC_CP14_LS] = kvm_handle_cp14_load_store, 97 [HSR_EC_CP14_64] = kvm_handle_cp14_access, 98 [HSR_EC_CP_0_13] = kvm_handle_cp_0_13_access, 99 [HSR_EC_CP10_ID] = kvm_handle_cp10_id, 100 [HSR_EC_SVC_HYP] = handle_svc_hyp, 101 [HSR_EC_HVC] = handle_hvc, 102 [HSR_EC_SMC] = handle_smc, 103 [HSR_EC_IABT] = kvm_handle_guest_abort, 104 [HSR_EC_IABT_HYP] = handle_pabt_hyp, 105 [HSR_EC_DABT] = kvm_handle_guest_abort, 106 [HSR_EC_DABT_HYP] = handle_dabt_hyp, 107}; 108 109static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu) 110{ 111 u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu); 112 113 if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) || 114 !arm_exit_handlers[hsr_ec]) { 115 kvm_err("Unknown exception class: hsr: %#08x\n", 116 (unsigned int)kvm_vcpu_get_hsr(vcpu)); 117 BUG(); 118 } 119 120 return arm_exit_handlers[hsr_ec]; 121} 122 123/* 124 * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on 125 * proper exit to userspace. 126 */ 127int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, 128 int exception_index) 129{ 130 exit_handle_fn exit_handler; 131 132 switch (exception_index) { 133 case ARM_EXCEPTION_IRQ: 134 return 1; 135 case ARM_EXCEPTION_UNDEFINED: 136 kvm_err("Undefined exception in Hyp mode at: %#08lx\n", 137 kvm_vcpu_get_hyp_pc(vcpu)); 138 BUG(); 139 panic("KVM: Hypervisor undefined exception!\n"); 140 case ARM_EXCEPTION_DATA_ABORT: 141 case ARM_EXCEPTION_PREF_ABORT: 142 case ARM_EXCEPTION_HVC: 143 /* 144 * See ARM ARM B1.14.1: "Hyp traps on instructions 145 * that fail their condition code check" 146 */ 147 if (!kvm_condition_valid(vcpu)) { 148 kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); 149 return 1; 150 } 151 152 exit_handler = kvm_get_exit_handler(vcpu); 153 154 return exit_handler(vcpu, run); 155 default: 156 kvm_pr_unimpl("Unsupported exception type: %d", 157 exception_index); 158 run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 159 return 0; 160 } 161}