• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2012 - Virtual Open Systems and Columbia University
4  * Author: Christoffer Dall <c.dall@virtualopensystems.com>
5  */
6 
7 #include <linux/kvm.h>
8 #include <linux/kvm_host.h>
9 #include <asm/kvm_emulate.h>
10 #include <asm/kvm_coproc.h>
11 #include <asm/kvm_mmu.h>
12 #include <kvm/arm_psci.h>
13 #include <trace/events/kvm.h>
14 
15 #include "trace.h"
16 
17 typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *);
18 
handle_hvc(struct kvm_vcpu * vcpu,struct kvm_run * run)19 static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)
20 {
21 	int ret;
22 
23 	trace_kvm_hvc(*vcpu_pc(vcpu), *vcpu_reg(vcpu, 0),
24 		      kvm_vcpu_hvc_get_imm(vcpu));
25 	vcpu->stat.hvc_exit_stat++;
26 
27 	ret = kvm_hvc_call_handler(vcpu);
28 	if (ret < 0) {
29 		vcpu_set_reg(vcpu, 0, ~0UL);
30 		return 1;
31 	}
32 
33 	return ret;
34 }
35 
handle_smc(struct kvm_vcpu * vcpu,struct kvm_run * run)36 static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)
37 {
38 	/*
39 	 * "If an SMC instruction executed at Non-secure EL1 is
40 	 * trapped to EL2 because HCR_EL2.TSC is 1, the exception is a
41 	 * Trap exception, not a Secure Monitor Call exception [...]"
42 	 *
43 	 * We need to advance the PC after the trap, as it would
44 	 * otherwise return to the same address...
45 	 */
46 	vcpu_set_reg(vcpu, 0, ~0UL);
47 	kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
48 	return 1;
49 }
50 
51 /**
52  * kvm_handle_wfx - handle a WFI or WFE instructions trapped in guests
53  * @vcpu:	the vcpu pointer
54  * @run:	the kvm_run structure pointer
55  *
56  * WFE: Yield the CPU and come back to this vcpu when the scheduler
57  * decides to.
58  * WFI: Simply call kvm_vcpu_block(), which will halt execution of
59  * world-switches and schedule other host processes until there is an
60  * incoming IRQ or FIQ to the VM.
61  */
kvm_handle_wfx(struct kvm_vcpu * vcpu,struct kvm_run * run)62 static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run)
63 {
64 	if (kvm_vcpu_get_hsr(vcpu) & HSR_WFI_IS_WFE) {
65 		trace_kvm_wfx(*vcpu_pc(vcpu), true);
66 		vcpu->stat.wfe_exit_stat++;
67 		kvm_vcpu_on_spin(vcpu, vcpu_mode_priv(vcpu));
68 	} else {
69 		trace_kvm_wfx(*vcpu_pc(vcpu), false);
70 		vcpu->stat.wfi_exit_stat++;
71 		kvm_vcpu_block(vcpu);
72 		kvm_clear_request(KVM_REQ_UNHALT, vcpu);
73 	}
74 
75 	kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu));
76 
77 	return 1;
78 }
79 
kvm_handle_unknown_ec(struct kvm_vcpu * vcpu,struct kvm_run * run)80 static int kvm_handle_unknown_ec(struct kvm_vcpu *vcpu, struct kvm_run *run)
81 {
82 	u32 hsr = kvm_vcpu_get_hsr(vcpu);
83 
84 	kvm_pr_unimpl("Unknown exception class: hsr: %#08x\n",
85 		      hsr);
86 
87 	kvm_inject_undefined(vcpu);
88 	return 1;
89 }
90 
91 static exit_handle_fn arm_exit_handlers[] = {
92 	[0 ... HSR_EC_MAX]	= kvm_handle_unknown_ec,
93 	[HSR_EC_WFI]		= kvm_handle_wfx,
94 	[HSR_EC_CP15_32]	= kvm_handle_cp15_32,
95 	[HSR_EC_CP15_64]	= kvm_handle_cp15_64,
96 	[HSR_EC_CP14_MR]	= kvm_handle_cp14_32,
97 	[HSR_EC_CP14_LS]	= kvm_handle_cp14_load_store,
98 	[HSR_EC_CP14_64]	= kvm_handle_cp14_64,
99 	[HSR_EC_CP_0_13]	= kvm_handle_cp_0_13_access,
100 	[HSR_EC_CP10_ID]	= kvm_handle_cp10_id,
101 	[HSR_EC_HVC]		= handle_hvc,
102 	[HSR_EC_SMC]		= handle_smc,
103 	[HSR_EC_IABT]		= kvm_handle_guest_abort,
104 	[HSR_EC_DABT]		= kvm_handle_guest_abort,
105 };
106 
kvm_get_exit_handler(struct kvm_vcpu * vcpu)107 static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu)
108 {
109 	u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
110 
111 	return arm_exit_handlers[hsr_ec];
112 }
113 
114 /*
115  * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on
116  * proper exit to userspace.
117  */
handle_exit(struct kvm_vcpu * vcpu,struct kvm_run * run,int exception_index)118 int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
119 		       int exception_index)
120 {
121 	exit_handle_fn exit_handler;
122 
123 	if (ARM_ABORT_PENDING(exception_index)) {
124 		u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu);
125 
126 		/*
127 		 * HVC/SMC already have an adjusted PC, which we need
128 		 * to correct in order to return to after having
129 		 * injected the abort.
130 		 */
131 		if (hsr_ec == HSR_EC_HVC || hsr_ec == HSR_EC_SMC) {
132 			u32 adj =  kvm_vcpu_trap_il_is32bit(vcpu) ? 4 : 2;
133 			*vcpu_pc(vcpu) -= adj;
134 		}
135 
136 		kvm_inject_vabt(vcpu);
137 		return 1;
138 	}
139 
140 	exception_index = ARM_EXCEPTION_CODE(exception_index);
141 
142 	switch (exception_index) {
143 	case ARM_EXCEPTION_IRQ:
144 		return 1;
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 	case ARM_EXCEPTION_DATA_ABORT:
159 		kvm_inject_vabt(vcpu);
160 		return 1;
161 	case ARM_EXCEPTION_HYP_GONE:
162 		/*
163 		 * HYP has been reset to the hyp-stub. This happens
164 		 * when a guest is pre-empted by kvm_reboot()'s
165 		 * shutdown call.
166 		 */
167 		run->exit_reason = KVM_EXIT_FAIL_ENTRY;
168 		return 0;
169 	default:
170 		kvm_pr_unimpl("Unsupported exception type: %d",
171 			      exception_index);
172 		run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
173 		return 0;
174 	}
175 }
176