• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (not much of an) Emulation layer for 32bit guests.
3  *
4  * Copyright (C) 2012,2013 - ARM Ltd
5  * Author: Marc Zyngier <marc.zyngier@arm.com>
6  *
7  * based on arch/arm/kvm/emulate.c
8  * Copyright (C) 2012 - Virtual Open Systems and Columbia University
9  * Author: Christoffer Dall <c.dall@virtualopensystems.com>
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include <linux/kvm_host.h>
25 #include <asm/kvm_emulate.h>
26 #include <asm/kvm_hyp.h>
27 
28 #ifndef CONFIG_ARM64
29 #define COMPAT_PSR_T_BIT	PSR_T_BIT
30 #define COMPAT_PSR_IT_MASK	PSR_IT_MASK
31 #endif
32 
33 /*
34  * stolen from arch/arm/kernel/opcodes.c
35  *
36  * condition code lookup table
37  * index into the table is test code: EQ, NE, ... LT, GT, AL, NV
38  *
39  * bit position in short is condition code: NZCV
40  */
41 static const unsigned short cc_map[16] = {
42 	0xF0F0,			/* EQ == Z set            */
43 	0x0F0F,			/* NE                     */
44 	0xCCCC,			/* CS == C set            */
45 	0x3333,			/* CC                     */
46 	0xFF00,			/* MI == N set            */
47 	0x00FF,			/* PL                     */
48 	0xAAAA,			/* VS == V set            */
49 	0x5555,			/* VC                     */
50 	0x0C0C,			/* HI == C set && Z clear */
51 	0xF3F3,			/* LS == C clear || Z set */
52 	0xAA55,			/* GE == (N==V)           */
53 	0x55AA,			/* LT == (N!=V)           */
54 	0x0A05,			/* GT == (!Z && (N==V))   */
55 	0xF5FA,			/* LE == (Z || (N!=V))    */
56 	0xFFFF,			/* AL always              */
57 	0			/* NV                     */
58 };
59 
60 /*
61  * Check if a trapped instruction should have been executed or not.
62  */
kvm_condition_valid32(const struct kvm_vcpu * vcpu)63 bool kvm_condition_valid32(const struct kvm_vcpu *vcpu)
64 {
65 	unsigned long cpsr;
66 	u32 cpsr_cond;
67 	int cond;
68 
69 	/* Top two bits non-zero?  Unconditional. */
70 	if (kvm_vcpu_get_hsr(vcpu) >> 30)
71 		return true;
72 
73 	/* Is condition field valid? */
74 	cond = kvm_vcpu_get_condition(vcpu);
75 	if (cond == 0xE)
76 		return true;
77 
78 	cpsr = *vcpu_cpsr(vcpu);
79 
80 	if (cond < 0) {
81 		/* This can happen in Thumb mode: examine IT state. */
82 		unsigned long it;
83 
84 		it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3);
85 
86 		/* it == 0 => unconditional. */
87 		if (it == 0)
88 			return true;
89 
90 		/* The cond for this insn works out as the top 4 bits. */
91 		cond = (it >> 4);
92 	}
93 
94 	cpsr_cond = cpsr >> 28;
95 
96 	if (!((cc_map[cond] >> cpsr_cond) & 1))
97 		return false;
98 
99 	return true;
100 }
101 
102 /**
103  * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block
104  * @vcpu:	The VCPU pointer
105  *
106  * When exceptions occur while instructions are executed in Thumb IF-THEN
107  * blocks, the ITSTATE field of the CPSR is not advanced (updated), so we have
108  * to do this little bit of work manually. The fields map like this:
109  *
110  * IT[7:0] -> CPSR[26:25],CPSR[15:10]
111  */
kvm_adjust_itstate(struct kvm_vcpu * vcpu)112 static void __hyp_text kvm_adjust_itstate(struct kvm_vcpu *vcpu)
113 {
114 	unsigned long itbits, cond;
115 	unsigned long cpsr = *vcpu_cpsr(vcpu);
116 	bool is_arm = !(cpsr & COMPAT_PSR_T_BIT);
117 
118 	if (is_arm || !(cpsr & COMPAT_PSR_IT_MASK))
119 		return;
120 
121 	cond = (cpsr & 0xe000) >> 13;
122 	itbits = (cpsr & 0x1c00) >> (10 - 2);
123 	itbits |= (cpsr & (0x3 << 25)) >> 25;
124 
125 	/* Perform ITAdvance (see page A2-52 in ARM DDI 0406C) */
126 	if ((itbits & 0x7) == 0)
127 		itbits = cond = 0;
128 	else
129 		itbits = (itbits << 1) & 0x1f;
130 
131 	cpsr &= ~COMPAT_PSR_IT_MASK;
132 	cpsr |= cond << 13;
133 	cpsr |= (itbits & 0x1c) << (10 - 2);
134 	cpsr |= (itbits & 0x3) << 25;
135 	*vcpu_cpsr(vcpu) = cpsr;
136 }
137 
138 /**
139  * kvm_skip_instr - skip a trapped instruction and proceed to the next
140  * @vcpu: The vcpu pointer
141  */
kvm_skip_instr32(struct kvm_vcpu * vcpu,bool is_wide_instr)142 void __hyp_text kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr)
143 {
144 	bool is_thumb;
145 
146 	is_thumb = !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_T_BIT);
147 	if (is_thumb && !is_wide_instr)
148 		*vcpu_pc(vcpu) += 2;
149 	else
150 		*vcpu_pc(vcpu) += 4;
151 	kvm_adjust_itstate(vcpu);
152 }
153