1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * (not much of an) Emulation layer for 32bit guests.
4  *
5  * Copyright (C) 2012,2013 - ARM Ltd
6  * Author: Marc Zyngier <marc.zyngier@arm.com>
7  *
8  * based on arch/arm/kvm/emulate.c
9  * Copyright (C) 2012 - Virtual Open Systems and Columbia University
10  * Author: Christoffer Dall <c.dall@virtualopensystems.com>
11  */
12 
13 #include <linux/kvm_host.h>
14 #include <asm/kvm_emulate.h>
15 #include <asm/kvm_hyp.h>
16 
17 /*
18  * Table taken from ARMv8 ARM DDI0487B-B, table G1-10.
19  */
20 static const u8 return_offsets[8][2] = {
21 	[0] = { 0, 0 },		/* Reset, unused */
22 	[1] = { 4, 2 },		/* Undefined */
23 	[2] = { 0, 0 },		/* SVC, unused */
24 	[3] = { 4, 4 },		/* Prefetch abort */
25 	[4] = { 8, 8 },		/* Data abort */
26 	[5] = { 0, 0 },		/* HVC, unused */
27 	[6] = { 4, 4 },		/* IRQ, unused */
28 	[7] = { 4, 4 },		/* FIQ, unused */
29 };
30 
prepare_fault32(struct kvm_vcpu * vcpu,u32 mode,u32 vect_offset)31 static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset)
32 {
33 	unsigned long cpsr;
34 	unsigned long new_spsr_value = *vcpu_cpsr(vcpu);
35 	bool is_thumb = (new_spsr_value & PSR_AA32_T_BIT);
36 	u32 return_offset = return_offsets[vect_offset >> 2][is_thumb];
37 	u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR);
38 
39 	cpsr = mode | PSR_AA32_I_BIT;
40 
41 	if (sctlr & (1 << 30))
42 		cpsr |= PSR_AA32_T_BIT;
43 	if (sctlr & (1 << 25))
44 		cpsr |= PSR_AA32_E_BIT;
45 
46 	*vcpu_cpsr(vcpu) = cpsr;
47 
48 	/* Note: These now point to the banked copies */
49 	vcpu_write_spsr(vcpu, new_spsr_value);
50 	*vcpu_reg32(vcpu, 14) = *vcpu_pc(vcpu) + return_offset;
51 
52 	/* Branch to exception vector */
53 	if (sctlr & (1 << 13))
54 		vect_offset += 0xffff0000;
55 	else /* always have security exceptions */
56 		vect_offset += vcpu_cp15(vcpu, c12_VBAR);
57 
58 	*vcpu_pc(vcpu) = vect_offset;
59 }
60 
kvm_inject_undef32(struct kvm_vcpu * vcpu)61 void kvm_inject_undef32(struct kvm_vcpu *vcpu)
62 {
63 	prepare_fault32(vcpu, PSR_AA32_MODE_UND, 4);
64 }
65 
66 /*
67  * Modelled after TakeDataAbortException() and TakePrefetchAbortException
68  * pseudocode.
69  */
inject_abt32(struct kvm_vcpu * vcpu,bool is_pabt,unsigned long addr)70 static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt,
71 			 unsigned long addr)
72 {
73 	u32 vect_offset;
74 	u32 *far, *fsr;
75 	bool is_lpae;
76 
77 	if (is_pabt) {
78 		vect_offset = 12;
79 		far = &vcpu_cp15(vcpu, c6_IFAR);
80 		fsr = &vcpu_cp15(vcpu, c5_IFSR);
81 	} else { /* !iabt */
82 		vect_offset = 16;
83 		far = &vcpu_cp15(vcpu, c6_DFAR);
84 		fsr = &vcpu_cp15(vcpu, c5_DFSR);
85 	}
86 
87 	prepare_fault32(vcpu, PSR_AA32_MODE_ABT | PSR_AA32_A_BIT, vect_offset);
88 
89 	*far = addr;
90 
91 	/* Give the guest an IMPLEMENTATION DEFINED exception */
92 	is_lpae = (vcpu_cp15(vcpu, c2_TTBCR) >> 31);
93 	if (is_lpae)
94 		*fsr = 1 << 9 | 0x34;
95 	else
96 		*fsr = 0x14;
97 }
98 
kvm_inject_dabt32(struct kvm_vcpu * vcpu,unsigned long addr)99 void kvm_inject_dabt32(struct kvm_vcpu *vcpu, unsigned long addr)
100 {
101 	inject_abt32(vcpu, false, addr);
102 }
103 
kvm_inject_pabt32(struct kvm_vcpu * vcpu,unsigned long addr)104 void kvm_inject_pabt32(struct kvm_vcpu *vcpu, unsigned long addr)
105 {
106 	inject_abt32(vcpu, true, addr);
107 }
108