-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathinject_exp.c
169 lines (150 loc) · 4.44 KB
/
inject_exp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
* SPDX-License-Identifier: BSD-3-Clause
* SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
*/
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <inject_exp.h>
#include <rec.h>
/*
* Calculate the address of the vector entry when an exception is inserted
* into the Realm.
*
* @vbar The base address of the vector table in the Realm.
* @spsr The Saved Program Status Register at EL2.
*/
static unsigned long calc_vector_entry(unsigned long vbar, unsigned long spsr)
{
unsigned long offset;
if ((spsr & MASK(SPSR_EL2_MODE)) == SPSR_EL2_MODE_EL1h) {
offset = VBAR_CEL_SP_ELx_OFFSET;
} else if ((spsr & MASK(SPSR_EL2_MODE)) == SPSR_EL2_MODE_EL1t) {
offset = VBAR_CEL_SP_EL0_OFFSET;
} else if ((spsr & MASK(SPSR_EL2_MODE)) == SPSR_EL2_MODE_EL0t) {
if ((spsr & MASK(SPSR_EL2_nRW)) == SPSR_EL2_nRW_AARCH64) {
offset = VBAR_LEL_AA64_OFFSET;
} else {
offset = VBAR_LEL_AA32_OFFSET;
}
} else {
assert(false);
offset = 0UL;
}
return vbar + offset;
}
/*
* Calculate the value of the pstate when an exception
* is inserted into the Realm.
*/
static unsigned long calc_pstate(void)
{
/*
* The pstate is EL1, AArch64, SPSel = SP_ELx and:
* DAIF = '1111b'
* NZCV = '0000b'
* TODO: setup TCO, DIT, UAO, PAN, SSBS, BTYPE
*/
unsigned long pstate = SPSR_EL2_MODE_EL1h |
SPSR_EL2_nRW_AARCH64 |
SPSR_EL2_F_BIT |
SPSR_EL2_I_BIT |
SPSR_EL2_A_BIT |
SPSR_EL2_D_BIT;
return pstate;
}
/*
* Calculate the content of the Realm's esr_el1 register when
* the Synchronous Instruction or Data Abort is injected into
* the Realm (EL1).
*
* The value is constructed from the @esr_el2 & @spsr_el2 that
* are captured when the exception from the Realm was taken to EL2.
*
* The fault status code (ESR_EL1.I/DFSC) is set to @fsc
*/
static unsigned long calc_esr_idabort(unsigned long esr_el2,
unsigned long spsr_el2,
unsigned long fsc)
{
/*
* Copy esr_el2 into esr_el1 apart from the following fields:
* - The exception class (EC). Its value depends on whether the
* exception to EL2 was from either EL1 or EL0.
* - I/DFSC. It will be set to @fsc.
* - FnV. It will set to zero.
* - S1PTW. It will be set to zero.
*/
unsigned long esr_el1 = esr_el2 & ~(MASK(ESR_EL2_EC) |
MASK(ESR_EL2_ABORT_FSC) |
ESR_EL2_ABORT_FNV_BIT |
ESR_EL2_ABORT_S1PTW_BIT);
unsigned long ec = esr_el2 & MASK(ESR_EL2_EC);
assert((ec == ESR_EL2_EC_INST_ABORT) || (ec == ESR_EL2_EC_DATA_ABORT));
if ((spsr_el2 & MASK(SPSR_EL2_MODE)) != SPSR_EL2_MODE_EL0t) {
ec += 1UL << ESR_EL2_EC_SHIFT;
}
esr_el1 |= ec;
/*
* Set the I/DFSC.
*/
assert((fsc & ~MASK(ESR_EL2_ABORT_FSC)) == 0UL);
esr_el1 |= fsc;
/*
* Set the EA.
*/
esr_el1 |= ESR_EL2_ABORT_EA_BIT;
return esr_el1;
}
/*
* Inject the Synchronous Instruction or Data Abort into the current REC.
* The I/DFSC field in the ESR_EL1 is set to @fsc
*/
void inject_sync_idabort(unsigned long fsc)
{
unsigned long esr_el2 = read_esr_el2();
unsigned long far_el2 = read_far_el2();
unsigned long elr_el2 = read_elr_el2();
unsigned long spsr_el2 = read_spsr_el2();
unsigned long vbar_el2 = read_vbar_el12();
unsigned long esr_el1 = calc_esr_idabort(esr_el2, spsr_el2, fsc);
unsigned long pc = calc_vector_entry(vbar_el2, spsr_el2);
unsigned long pstate = calc_pstate();
write_far_el12(far_el2);
write_elr_el12(elr_el2);
write_spsr_el12(spsr_el2);
write_esr_el12(esr_el1);
write_elr_el2(pc);
write_spsr_el2(pstate);
}
/*
* Inject the Synchronous Instruction or Data Abort into @rec.
* The I/DFSC field in the ESR_EL1 is set to @fsc
*/
void inject_sync_idabort_rec(struct rec *rec, unsigned long fsc)
{
rec->sysregs.far_el1 = rec->last_run_info.far;
rec->sysregs.elr_el1 = rec->pc;
rec->sysregs.spsr_el1 = rec->pstate;
rec->sysregs.esr_el1 = calc_esr_idabort(rec->last_run_info.esr,
rec->pstate, fsc);
rec->pc = calc_vector_entry(rec->sysregs.vbar_el1, rec->pstate);
rec->pstate = calc_pstate();
}
/*
* Inject the Undefined Synchronous Exception into the current REC.
*/
void realm_inject_undef_abort(void)
{
unsigned long esr = MASK(ESR_EL2_IL) | ESR_EL2_EC_UNKNOWN;
unsigned long elr = read_elr_el2();
unsigned long spsr = read_spsr_el2();
unsigned long vbar = read_vbar_el12();
unsigned long pc = calc_vector_entry(vbar, spsr);
unsigned long pstate = calc_pstate();
write_elr_el12(elr);
write_spsr_el12(spsr);
write_esr_el12(esr);
write_elr_el2(pc);
write_spsr_el2(pstate);
}