1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #ifndef __ARCH_ARM64_TRANSITION_H__
4 #define __ARCH_ARM64_TRANSITION_H__
5
6 /* ======================== Transition Library =================================
7 * Transition library provides two main functionalities:
8 * 1) It allows any program X to be executed at EL Y using the state Z. It
9 * provides struct exc_state which holds the state of the EL to which we want to
10 * execute X at. Before performing an eret to the entry point of the program X,
11 * it initializes required registers using this exc_state structure. Here, X0 =
12 * args to the program X. IMP!!! : We do not initialize SP_EL0 for the program
13 * X, the program will have to handle that on its own. This is because while
14 * performing an eret to X, we could make SP_EL0 point to regs structure which
15 * then follows common exception exit path.
16 * 2) It serves as a common mechanism for handling exception entry and exit at
17 * any given EL. On entry to an exception, SP_ELx is selected by default. The
18 * exc entry routine stores all xregs and jumps to exc_entry which
19 * saves ELR, SPSR, EL, Mode and other information about the state from which
20 * exception was generated. On exit, xregs are restored by unwinding of SP_ELx.
21 * =============================================================================
22 */
23
24 /* Macros for EL mode in SPSR */
25 #define STACK_POP_BYTES 16
26 #define STACK_PUSH_BYTES -16
27
28 #define EXC_VID_CUR_SP_EL0_SYNC 0
29 #define EXC_VID_CUR_SP_EL0_IRQ 1
30 #define EXC_VID_CUR_SP_EL0_FIRQ 2
31 #define EXC_VID_CUR_SP_EL0_SERR 3
32 #define EXC_VID_CUR_SP_ELX_SYNC 4
33 #define EXC_VID_CUR_SP_ELX_IRQ 5
34 #define EXC_VID_CUR_SP_ELX_FIQ 6
35 #define EXC_VID_CUR_SP_ELX_SERR 7
36 #define EXC_VID_LOW64_SYNC 8
37 #define EXC_VID_LOW64_IRQ 9
38 #define EXC_VID_LOW64_FIQ 10
39 #define EXC_VID_LOW64_SERR 11
40 #define EXC_VID_LOW32_SYNC 12
41 #define EXC_VID_LOW32_IRQ 13
42 #define EXC_VID_LOW32_FIQ 14
43 #define EXC_VID_LOW32_SERR 15
44 #define NUM_EXC_VIDS 16
45
46 #ifndef __ASSEMBLER__
47
48 #include <stdint.h>
49 #include <arch/lib_helpers.h>
50
51 #define XI_INDEX(i) X##i##_INDEX = i
52
53 enum {
54 XI_INDEX(0),
55 XI_INDEX(1),
56 XI_INDEX(2),
57 XI_INDEX(3),
58 XI_INDEX(4),
59 XI_INDEX(5),
60 XI_INDEX(6),
61 XI_INDEX(7),
62 XI_INDEX(8),
63 XI_INDEX(9),
64 XI_INDEX(10),
65 XI_INDEX(11),
66 XI_INDEX(12),
67 XI_INDEX(13),
68 XI_INDEX(14),
69 XI_INDEX(15),
70 XI_INDEX(16),
71 XI_INDEX(17),
72 XI_INDEX(18),
73 XI_INDEX(19),
74 XI_INDEX(20),
75 XI_INDEX(21),
76 XI_INDEX(22),
77 XI_INDEX(23),
78 XI_INDEX(24),
79 XI_INDEX(25),
80 XI_INDEX(26),
81 XI_INDEX(27),
82 XI_INDEX(28),
83 XI_INDEX(29),
84 XI_INDEX(30),
85 XMAX_INDEX,
86 };
87
88 /*
89 * Important: Any changes made to the two structures below should reflect in the
90 * exc_prologue and exc_exit routines in transition_asm.S
91 */
92 struct regs {
93 uint64_t sp;
94 uint64_t x[31];
95 };
96
97 struct elx_state {
98 uint64_t spsr;
99 uint64_t sp_el0;
100 uint64_t sp_elx;
101 uint64_t elr;
102 };
103
104 struct exc_state {
105 struct elx_state elx;
106 struct regs regs;
107 };
108
109 /*
110 * get_eret_EL returns the value of the exception state to which we will be
111 * returning. This value is saved in SPSR before performing an eret.
112 *
113 * Exception mode is defined by M[3:0] bits in SPSR:
114 * ( M[3:2] = EL, M[1] = unused, M[0] = t/h mode for stack
115 *
116 * 0b0000 EL0t
117 * 0b0100 EL1t
118 * 0b0101 EL1h
119 * 0b1000 EL2t
120 * 0b1001 EL2h
121 * 0b1100 EL3t
122 * 0b1101 EL3h
123 */
124
get_eret_el(uint8_t el,uint8_t l_or_h)125 static inline uint8_t get_eret_el(uint8_t el, uint8_t l_or_h)
126 {
127 uint8_t el_mode = el << CURRENT_EL_SHIFT;
128
129 el_mode |= l_or_h;
130
131 return el_mode;
132 }
133
get_el_from_spsr(uint64_t spsr)134 static inline uint8_t get_el_from_spsr(uint64_t spsr)
135 {
136 return ((spsr >> CURRENT_EL_SHIFT) & CURRENT_EL_MASK);
137 }
138
get_mode_from_spsr(uint64_t spsr)139 static inline uint8_t get_mode_from_spsr(uint64_t spsr)
140 {
141 return (spsr & SPSR_L_H_MASK);
142 }
143
144 /* Transitions supported are:
145 * 1. elx --> elx - 1
146 * 2. Transitions to aarch64 state
147 *
148 * Other than this, if any transition needs to be supported, relevant changes
149 * need to be done to hcr/scr registers.
150 */
151
152 /*
153 * Transitions to EL2 with given entry point and argument in X0. SPSR can be
154 * partially configured, but the exception level given must be EL2.
155 */
156 void transition_to_el2(void *entry, void *arg, uint64_t spsr);
157
158 /*
159 * exc_exit it called while returning from an exception. It expects pointer to
160 * the regs structure on stack so that it can unwind the used stack.
161 */
162 void exc_exit(struct regs *regs);
163 /*
164 * trans_switch is called by the non-exception path i.e. transition C code
165 * while making a transition to lower EL. It select L mode so that SP_EL0 is
166 * used during the unwinding in exc_exit.
167 */
168 void trans_switch(struct regs *regs);
169 /* exc_set_vbar sets up the vbar for exception vectors. */
170 void exc_set_vbar(void);
171
172 /* exc_dispatch is the user-defined exception handler. */
173 void exc_dispatch(struct exc_state *exc_state, uint64_t id);
174 /*
175 * exc_entry is the C based component of the exception entry before we
176 * jump to user-defined handler. This initializes all the regs in elx_state and
177 * also sets the sp value in regs structure.
178 */
179 void exc_entry(struct exc_state *exc_state, uint64_t id);
180
181 #endif /* __ASSEMBLER__ */
182
183 #endif /* __ARCH_ARM64_TRANSITION_H__ */
184