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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
|
/*
* This file is part of the coreboot project.
*
* Copyright 2014 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __ARCH_ARM64_TRANSITION_H__
#define __ARCH_ARM64_TRANSITION_H__
/* ======================== Transition Library =================================
* Transition library provides two main functionalities:
* 1) It allows any program X to be executed at EL Y using the state Z. It
* provides struct exc_state which holds the state of the EL to which we want to
* execute X at. Before performing an eret to the entry point of the program X,
* it initializes required registers using this exc_state structure. Here, X0 =
* args to the program X. IMP!!! : We do not initialize SP_EL0 for the program
* X, the program will have to handle that on its own. This is because while
* performing an eret to X, we could make SP_EL0 point to regs structure which
* then follows common exception exit path.
* 2) It serves as a common mechanism for handling exception entry and exit at
* any given EL. On entry to an exception, SP_ELx is selected by default. The
* exc entry routine stores all xregs and jumps to exc_entry which
* saves ELR, SPSR, EL, Mode and other information about the state from which
* exception was generated. On exit, xregs are restored by unwinding of SP_ELx.
* =============================================================================
*/
/* Macros for EL mode in SPSR */
#define STACK_POP_BYTES 16
#define STACK_PUSH_BYTES -16
#define EXC_VID_CUR_SP_EL0_SYNC 0
#define EXC_VID_CUR_SP_EL0_IRQ 1
#define EXC_VID_CUR_SP_EL0_FIRQ 2
#define EXC_VID_CUR_SP_EL0_SERR 3
#define EXC_VID_CUR_SP_ELX_SYNC 4
#define EXC_VID_CUR_SP_ELX_IRQ 5
#define EXC_VID_CUR_SP_ELX_FIQ 6
#define EXC_VID_CUR_SP_ELX_SERR 7
#define EXC_VID_LOW64_SYNC 8
#define EXC_VID_LOW64_IRQ 9
#define EXC_VID_LOW64_FIQ 10
#define EXC_VID_LOW64_SERR 11
#define EXC_VID_LOW32_SYNC 12
#define EXC_VID_LOW32_IRQ 13
#define EXC_VID_LOW32_FIQ 14
#define EXC_VID_LOW32_SERR 15
#define NUM_EXC_VIDS 16
#ifndef __ASSEMBLER__
#include <stdint.h>
#include <arch/lib_helpers.h>
#define XI_INDEX(i) X##i##_INDEX = i
enum {
XI_INDEX(0),
XI_INDEX(1),
XI_INDEX(2),
XI_INDEX(3),
XI_INDEX(4),
XI_INDEX(5),
XI_INDEX(6),
XI_INDEX(7),
XI_INDEX(8),
XI_INDEX(9),
XI_INDEX(10),
XI_INDEX(11),
XI_INDEX(12),
XI_INDEX(13),
XI_INDEX(14),
XI_INDEX(15),
XI_INDEX(16),
XI_INDEX(17),
XI_INDEX(18),
XI_INDEX(19),
XI_INDEX(20),
XI_INDEX(21),
XI_INDEX(22),
XI_INDEX(23),
XI_INDEX(24),
XI_INDEX(25),
XI_INDEX(26),
XI_INDEX(27),
XI_INDEX(28),
XI_INDEX(29),
XI_INDEX(30),
XMAX_INDEX,
};
/*
* Important: Any changes made to the two structures below should reflect in the
* exc_prologue and exc_exit routines in transition_asm.S
*/
struct regs {
uint64_t sp;
uint64_t x[31];
};
struct elx_state {
uint64_t spsr;
uint64_t sp_el0;
uint64_t sp_elx;
uint64_t elr;
};
struct exc_state {
struct elx_state elx;
struct regs regs;
};
/*
* get_eret_EL returns the value of the exception state to which we will be
* returning. This value is saved in SPSR before performing an eret.
*
* Exception mode is defined by M[3:0] bits in SPSR:
* ( M[3:2] = EL, M[1] = unused, M[0] = t/h mode for stack
*
* 0b0000 EL0t
* 0b0100 EL1t
* 0b0101 EL1h
* 0b1000 EL2t
* 0b1001 EL2h
* 0b1100 EL3t
* 0b1101 EL3h
*/
static inline uint8_t get_eret_el(uint8_t el, uint8_t l_or_h)
{
uint8_t el_mode = el << CURRENT_EL_SHIFT;
el_mode |= l_or_h;
return el_mode;
}
static inline uint8_t get_el_from_spsr(uint64_t spsr)
{
return ((spsr >> CURRENT_EL_SHIFT) & CURRENT_EL_MASK);
}
static inline uint8_t get_mode_from_spsr(uint64_t spsr)
{
return (spsr & SPSR_L_H_MASK);
}
/* Transitions supported are:
* 1. elx --> elx - 1
* 2. Transitions to aarch64 state
*
* Other than this, if any transition needs to be supported, relevant changes
* need to be done to hcr/scr registers.
*/
/*
* Transitions to EL2 with given entry point and argument in X0. SPSR can be
* partially configured, but the exception level given must be EL2.
*/
void transition_to_el2(void *entry, void *arg, uint64_t spsr);
/*
* exc_exit it called while returning from an exception. It expects pointer to
* the regs structure on stack so that it can unwind the used stack.
*/
void exc_exit(struct regs *regs);
/*
* trans_switch is called by the non-exception path i.e. transition C code
* while making a transition to lower EL. It select L mode so that SP_EL0 is
* used during the unwinding in exc_exit.
*/
void trans_switch(struct regs *regs);
/* exc_set_vbar sets up the vbar for exception vectors. */
void exc_set_vbar(void);
/* exc_dispatch is the user-defined exception handler. */
void exc_dispatch(struct exc_state *exc_state, uint64_t id);
/*
* exc_entry is the C based component of the exception entry before we
* jump to user-defined handler. This initializes all the regs in elx_state and
* also sets the sp value in regs structure.
*/
void exc_entry(struct exc_state *exc_state, uint64_t id);
#endif /* __ASSEMBLER__ */
#endif /* __ARCH_ARM64_TRANSITION_H__ */
|