xref: /aosp_15_r20/external/coreboot/src/arch/arm64/include/arch/transition.h (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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