Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.11 169 lines 4.5 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 29typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *); 30 31static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) 32{ 33 int ret; 34 35 trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0), 36 kvm_vcpu_hvc_get_imm(vcpu)); 37 vcpu->stat.hvc_exit_stat++; 38 39 ret = kvm_psci_call(vcpu); 40 if (ret < 0) { 41 kvm_inject_undefined(vcpu); 42 return 1; 43 } 44 45 return ret; 46} 47 48static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) 49{ 50 kvm_inject_undefined(vcpu); 51 return 1; 52} 53 54/** 55 * kvm_handle_wfx - handle a WFI or WFE instructions trapped in guests 56 * @vcpu: the vcpu pointer 57 * @run: the kvm_run structure pointer 58 * 59 * WFE: Yield the CPU and come back to this vcpu when the scheduler 60 * decides to. 61 * WFI: Simply call kvm_vcpu_block(), which will halt execution of 62 * world-switches and schedule other host processes until there is an 63 * incoming IRQ or FIQ to the VM. 64 */ 65static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run) 66{ 67 if (kvm_vcpu_get_hsr(vcpu) & HSR_WFI_IS_WFE) { 68 trace_kvm_wfx(*vcpu_pc(vcpu), true); 69 vcpu->stat.wfe_exit_stat++; 70 kvm_vcpu_on_spin(vcpu); 71 } else { 72 trace_kvm_wfx(*vcpu_pc(vcpu), false); 73 vcpu->stat.wfi_exit_stat++; 74 kvm_vcpu_block(vcpu); 75 } 76 77 kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); 78 79 return 1; 80} 81 82static int kvm_handle_unknown_ec(struct kvm_vcpu *vcpu, struct kvm_run *run) 83{ 84 u32 hsr = kvm_vcpu_get_hsr(vcpu); 85 86 kvm_pr_unimpl("Unknown exception class: hsr: %#08x\n", 87 hsr); 88 89 kvm_inject_undefined(vcpu); 90 return 1; 91} 92 93static exit_handle_fn arm_exit_handlers[] = { 94 [0 ... HSR_EC_MAX] = kvm_handle_unknown_ec, 95 [HSR_EC_WFI] = kvm_handle_wfx, 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_HVC] = handle_hvc, 104 [HSR_EC_SMC] = handle_smc, 105 [HSR_EC_IABT] = kvm_handle_guest_abort, 106 [HSR_EC_DABT] = kvm_handle_guest_abort, 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 return arm_exit_handlers[hsr_ec]; 114} 115 116/* 117 * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on 118 * proper exit to userspace. 119 */ 120int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, 121 int exception_index) 122{ 123 exit_handle_fn exit_handler; 124 125 if (ARM_ABORT_PENDING(exception_index)) { 126 u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu); 127 128 /* 129 * HVC/SMC already have an adjusted PC, which we need 130 * to correct in order to return to after having 131 * injected the abort. 132 */ 133 if (hsr_ec == HSR_EC_HVC || hsr_ec == HSR_EC_SMC) { 134 u32 adj = kvm_vcpu_trap_il_is32bit(vcpu) ? 4 : 2; 135 *vcpu_pc(vcpu) -= adj; 136 } 137 138 kvm_inject_vabt(vcpu); 139 return 1; 140 } 141 142 exception_index = ARM_EXCEPTION_CODE(exception_index); 143 144 switch (exception_index) { 145 case ARM_EXCEPTION_IRQ: 146 return 1; 147 case ARM_EXCEPTION_HVC: 148 /* 149 * See ARM ARM B1.14.1: "Hyp traps on instructions 150 * that fail their condition code check" 151 */ 152 if (!kvm_condition_valid(vcpu)) { 153 kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); 154 return 1; 155 } 156 157 exit_handler = kvm_get_exit_handler(vcpu); 158 159 return exit_handler(vcpu, run); 160 case ARM_EXCEPTION_DATA_ABORT: 161 kvm_inject_vabt(vcpu); 162 return 1; 163 default: 164 kvm_pr_unimpl("Unsupported exception type: %d", 165 exception_index); 166 run->exit_reason = KVM_EXIT_INTERNAL_ERROR; 167 return 0; 168 } 169}