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