OP-TEE: Interrupts / Exceptions

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca Currently, RISC-V OP-TEE doesn’t define native interrupts. All interrupts are foreign interrupts: 1 2 3 4 5 6 // core/arch/riscv/include/kernel/thread_arch.h #define THREAD_EXCP_FOREIGN_INTR (CSR_XIE_SIE | CSR_XIE_TIE | CSR_XIE_EIE) #define THREAD_EXCP_NATIVE_INTR (0) #define THREAD_EXCP_ALL (THREAD_EXCP_FOREIGN_INTR |\ THREAD_EXCP_NATIVE_INTR) i.e. OP-TEE DOES NOT handle any interrupts. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // arch/riscv/kernel/thread_rv.S FUNC thread_trap_vect , : // mscratch/sscratch = 0: Trap is from kernel. // mscratch/sscratch = 1: Trap is from user. csrrw tp, CSR_XSCRATCH, tp bnez tp, 0f /* Read tp back */ csrrw tp, CSR_XSCRATCH, tp j [trap_from_kernel](/posts/optee-interrupts/) 0: /* Now tp is [thread_core_local](https://app.notion.com/p/KVM_SET_USER_MEMORY_REGION-817fabdad8494e53b35403ee09ff4b8f?pvs=21) */ j [trap_from_user](/posts/optee-interrupts/) thread_trap_vect_end: END_FUNC thread_trap_vect 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 // arch/riscv/kernel/thread_vs.S LOCAL_FUNC trap_from_kernel, : /* Save sp, a0, a1 into temporary spaces of thread_core_local */ store_xregs tp, THREAD_CORE_LOCAL_X0, REG_SP store_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1 csrr a0, CSR_XCAUSE /* MSB of cause differentiates between interrupts and exceptions */ // P.S. bge is signed comparison. bge a0, zero, exception_from_kernel interrupt_from_kernel: /* Get thread context as sp */ get_thread_ctx sp, a0 /* Load and save kernel sp */ load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0 store_xregs sp, THREAD_CTX_REG_SP, REG_A0 /* Restore user a0, a1 which can be saved later */ load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1 /* Save all other GPRs */ store_xregs sp, THREAD_CTX_REG_RA, REG_RA store_xregs sp, THREAD_CTX_REG_GP, REG_GP store_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2 store_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1 store_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7 store_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11 store_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6 /* Save XIE */ csrr t0, CSR_XIE store_xregs sp, THREAD_CTX_REG_IE, REG_T0 /* Mask all interrupts */ csrw CSR_XIE, x0 /* Save XSTATUS */ csrr t0, CSR_XSTATUS store_xregs sp, THREAD_CTX_REG_STATUS, REG_T0 /* Save XEPC */ csrr t0, CSR_XEPC store_xregs sp, THREAD_CTX_REG_EPC, REG_T0 /* * a0 = cause * a1 = sp * Call thread_interrupt_handler(cause, regs) */ csrr a0, CSR_XCAUSE mv a1, sp /* Load tmp_stack_va_end as current sp. */ load_xregs tp, THREAD_CORE_LOCAL_TMP_STACK_VA_END, REG_SP call [thread_interrupt_handler](/posts/optee-interrupts/) /* Get thread context as sp */ get_thread_ctx sp, t0 /* Restore XEPC */ load_xregs sp, THREAD_CTX_REG_EPC, REG_T0 csrw CSR_XEPC, t0 /* Restore XIE */ load_xregs sp, THREAD_CTX_REG_IE, REG_T0 csrw CSR_XIE, t0 /* Restore XSTATUS */ load_xregs sp, THREAD_CTX_REG_STATUS, REG_T0 csrw CSR_XSTATUS, t0 /* Set scratch as thread_core_local */ csrw CSR_XSCRATCH, tp /* Restore all GPRs */ load_xregs sp, THREAD_CTX_REG_RA, REG_RA load_xregs sp, THREAD_CTX_REG_GP, REG_GP load_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2 load_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1 load_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7 load_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11 load_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6 load_xregs sp, THREAD_CTX_REG_SP, REG_SP XRET exception_from_kernel: /* * Update core local flags. * flags = (flags << THREAD_CLF_SAVED_SHIFT) | THREAD_CLF_ABORT; */ lw a0, THREAD_CORE_LOCAL_FLAGS(tp) slli a0, a0, THREAD_CLF_SAVED_SHIFT ori a0, a0, THREAD_CLF_ABORT li a1, (THREAD_CLF_ABORT << THREAD_CLF_SAVED_SHIFT) and a1, a0, a1 bnez a1, sel_tmp_sp /* Select abort stack */ load_xregs tp, THREAD_CORE_LOCAL_ABT_STACK_VA_END, REG_A1 j set_sp sel_tmp_sp: /* We have an abort while using the abort stack, select tmp stack */ load_xregs tp, THREAD_CORE_LOCAL_TMP_STACK_VA_END, REG_A1 ori a0, a0, THREAD_CLF_TMP /* flags |= THREAD_CLF_TMP; */ set_sp: mv sp, a1 sw a0, THREAD_CORE_LOCAL_FLAGS(tp) /* * Save state on stack */ addi sp, sp, -THREAD_ABT_REGS_SIZE /* Save kernel sp */ load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0 store_xregs sp, THREAD_ABT_REG_SP, REG_A0 /* Restore kernel a0, a1 which can be saved later */ load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1 /* Save all other GPRs */ store_xregs sp, THREAD_ABT_REG_RA, REG_RA store_xregs sp, THREAD_ABT_REG_GP, REG_GP store_xregs sp, THREAD_ABT_REG_TP, REG_TP store_xregs sp, THREAD_ABT_REG_T0, REG_T0, REG_T2 store_xregs sp, THREAD_ABT_REG_S0, REG_S0, REG_S1 store_xregs sp, THREAD_ABT_REG_A0, REG_A0, REG_A7 store_xregs sp, THREAD_ABT_REG_S2, REG_S2, REG_S11 store_xregs sp, THREAD_ABT_REG_T3, REG_T3, REG_T6 /* Save XIE */ csrr t0, CSR_XIE store_xregs sp, THREAD_ABT_REG_IE, REG_T0 /* Mask all interrupts */ csrw CSR_XIE, x0 /* Save XSTATUS */ csrr t0, CSR_XSTATUS store_xregs sp, THREAD_ABT_REG_STATUS, REG_T0 /* Save XEPC */ csrr t0, CSR_XEPC store_xregs sp, THREAD_ABT_REG_EPC, REG_T0 /* Save XTVAL */ csrr t0, CSR_XTVAL store_xregs sp, THREAD_ABT_REG_TVAL, REG_T0 /* Save XCAUSE */ csrr a0, CSR_XCAUSE store_xregs sp, THREAD_ABT_REG_CAUSE, REG_A0 /* * a0 = cause * a1 = sp (struct thread_abort_regs *regs) * Call abort_handler(cause, regs) */ mv a1, sp call abort_handler /* * Restore state from stack */ /* Restore XEPC */ load_xregs sp, THREAD_ABT_REG_EPC, REG_T0 csrw CSR_XEPC, t0 /* Restore XIE */ load_xregs sp, THREAD_ABT_REG_IE, REG_T0 csrw CSR_XIE, t0 /* Restore XSTATUS */ load_xregs sp, THREAD_ABT_REG_STATUS, REG_T0 csrw CSR_XSTATUS, t0 /* Set scratch as thread_core_local */ csrw CSR_XSCRATCH, tp /* Update core local flags */ lw a0, THREAD_CORE_LOCAL_FLAGS(tp) srli a0, a0, THREAD_CLF_SAVED_SHIFT sw a0, THREAD_CORE_LOCAL_FLAGS(tp) /* Restore all GPRs */ load_xregs sp, THREAD_ABT_REG_RA, REG_RA load_xregs sp, THREAD_ABT_REG_GP, REG_GP load_xregs sp, THREAD_ABT_REG_TP, REG_TP load_xregs sp, THREAD_ABT_REG_T0, REG_T0, REG_T2 load_xregs sp, THREAD_ABT_REG_S0, REG_S0, REG_S1 load_xregs sp, THREAD_ABT_REG_A0, REG_A0, REG_A7 load_xregs sp, THREAD_ABT_REG_S2, REG_S2, REG_S11 load_xregs sp, THREAD_ABT_REG_T3, REG_T3, REG_T6 load_xregs sp, THREAD_ABT_REG_SP, REG_SP XRET END_FUNC trap_from_kernel 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 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 // arch/riscv/kernel/thread_rv.S LOCAL_FUNC trap_from_user, : /* Save user sp, a0, a1 into temporary spaces of thread_core_local */ store_xregs tp, THREAD_CORE_LOCAL_X0, REG_SP store_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1 csrr a0, CSR_XCAUSE /* MSB of cause differentiates between interrupts and exceptions */ bge a0, zero, exception_from_user interrupt_from_user: /* Get thread context as sp */ get_thread_ctx sp, a0 /* Save user sp */ load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0 store_xregs sp, THREAD_CTX_REG_SP, REG_A0 /* Restore user a0, a1 which can be saved later */ load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1 /* Save user gp */ store_xregs sp, THREAD_CTX_REG_GP, REG_GP /* * Set the scratch register to 0 such in case of a recursive * exception thread_trap_vect() knows that it is emitted from kernel. */ csrrw gp, CSR_XSCRATCH, zero /* Save user tp we previously swapped into CSR_XSCRATCH */ store_xregs sp, THREAD_CTX_REG_TP, REG_GP /* Set kernel gp */ .option push .option norelax la gp, __global_pointer$ .option pop /* Save all other GPRs */ store_xregs sp, THREAD_CTX_REG_RA, REG_RA store_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2 store_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1 store_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7 store_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11 store_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6 /* Save XIE */ csrr t0, CSR_XIE store_xregs sp, THREAD_CTX_REG_IE, REG_T0 /* Mask all interrupts */ csrw CSR_XIE, x0 /* Save XSTATUS */ csrr t0, CSR_XSTATUS store_xregs sp, THREAD_CTX_REG_STATUS, REG_T0 /* Save XEPC */ csrr t0, CSR_XEPC store_xregs sp, THREAD_CTX_REG_EPC, REG_T0 /* * a0 = cause * a1 = sp * Call thread_interrupt_handler(cause, regs) */ csrr a0, CSR_XCAUSE mv a1, sp /* Load tmp_stack_va_end as current sp. */ load_xregs tp, THREAD_CORE_LOCAL_TMP_STACK_VA_END, REG_SP call [thread_interrupt_handler](/posts/optee-interrupts/) /* Get thread context as sp */ get_thread_ctx sp, t0 /* Restore XEPC */ load_xregs sp, THREAD_CTX_REG_EPC, REG_T0 csrw CSR_XEPC, t0 /* Restore XIE */ load_xregs sp, THREAD_CTX_REG_IE, REG_T0 csrw CSR_XIE, t0 /* Restore XSTATUS */ load_xregs sp, THREAD_CTX_REG_STATUS, REG_T0 csrw CSR_XSTATUS, t0 /* Set scratch as thread_core_local */ csrw CSR_XSCRATCH, tp /* Restore all GPRs */ load_xregs sp, THREAD_CTX_REG_RA, REG_RA load_xregs sp, THREAD_CTX_REG_GP, REG_GP load_xregs sp, THREAD_CTX_REG_TP, REG_TP load_xregs sp, THREAD_CTX_REG_T0, REG_T0, REG_T2 load_xregs sp, THREAD_CTX_REG_S0, REG_S0, REG_S1 load_xregs sp, THREAD_CTX_REG_A0, REG_A0, REG_A7 load_xregs sp, THREAD_CTX_REG_S2, REG_S2, REG_S11 load_xregs sp, THREAD_CTX_REG_T3, REG_T3, REG_T6 load_xregs sp, THREAD_CTX_REG_SP, REG_SP XRET exception_from_user: /* a0 is CSR_XCAUSE */ li a1, CAUSE_USER_ECALL bne a0, a1, abort_from_user ecall_from_user: /* Load and set kernel sp from thread context */ get_thread_ctx a0, a1 load_xregs a0, THREAD_CTX_KERN_SP, REG_SP /* Now sp is kernel sp, create stack for struct thread_scall_regs */ addi sp, sp, -THREAD_SCALL_REGS_SIZE /* Save user sp */ load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0 store_xregs sp, THREAD_SCALL_REG_SP, REG_A0 /* Restore user a0, a1 which can be saved later */ load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1 /* Save user gp */ store_xregs sp, THREAD_SCALL_REG_GP, REG_GP /* * Set the scratch register to 0 such in case of a recursive * exception thread_trap_vect() knows that it is emitted from kernel. */ csrrw gp, CSR_XSCRATCH, zero /* Save user tp we previously swapped into CSR_XSCRATCH */ store_xregs sp, THREAD_SCALL_REG_TP, REG_GP /* Set kernel gp */ .option push .option norelax la gp, __global_pointer$ .option pop /* Save other caller-saved registers */ store_xregs sp, THREAD_SCALL_REG_RA, REG_RA store_xregs sp, THREAD_SCALL_REG_T0, REG_T0, REG_T2 store_xregs sp, THREAD_SCALL_REG_A0, REG_A0, REG_A7 store_xregs sp, THREAD_SCALL_REG_T3, REG_T3, REG_T6 /* Save XIE */ csrr a0, CSR_XIE store_xregs sp, THREAD_SCALL_REG_IE, REG_A0 /* Mask all interrupts */ csrw CSR_XIE, zero /* Save XSTATUS */ csrr a0, CSR_XSTATUS store_xregs sp, THREAD_SCALL_REG_STATUS, REG_A0 /* Save XEPC */ csrr a0, CSR_XEPC store_xregs sp, THREAD_SCALL_REG_EPC, REG_A0 /* * a0 = struct thread_scall_regs *regs * Call thread_scall_handler(regs) */ mv a0, sp call [thread_scall_handler](/posts/optee-interrupts/) /* * Save kernel sp we'll had at the beginning of this function. * This is when this TA has called another TA because * __thread_enter_user_mode() also saves the stack pointer in this * field. */ get_thread_ctx a0, a1 addi t0, sp, THREAD_SCALL_REGS_SIZE store_xregs a0, THREAD_CTX_KERN_SP, REG_T0 /* * We are returning to U-Mode, on return, the program counter * is set to xsepc (pc=xepc), we add 4 (size of an instruction) * to continue to next instruction. */ load_xregs sp, THREAD_SCALL_REG_EPC, REG_T0 addi t0, t0, 4 csrw CSR_XEPC, t0 /* Restore XIE */ load_xregs sp, THREAD_SCALL_REG_IE, REG_T0 csrw CSR_XIE, t0 /* Restore XSTATUS */ load_xregs sp, THREAD_SCALL_REG_STATUS, REG_T0 csrw CSR_XSTATUS, t0 /* Set scratch as thread_core_local */ csrw CSR_XSCRATCH, tp /* Restore caller-saved registers */ load_xregs sp, THREAD_SCALL_REG_RA, REG_RA load_xregs sp, THREAD_SCALL_REG_GP, REG_GP load_xregs sp, THREAD_SCALL_REG_TP, REG_TP load_xregs sp, THREAD_SCALL_REG_T0, REG_T0, REG_T2 load_xregs sp, THREAD_SCALL_REG_A0, REG_A0, REG_A7 load_xregs sp, THREAD_SCALL_REG_T3, REG_T3, REG_T6 load_xregs sp, THREAD_SCALL_REG_SP, REG_SP XRET abort_from_user: /* * Update core local flags */ lw a0, THREAD_CORE_LOCAL_FLAGS(tp) slli a0, a0, THREAD_CLF_SAVED_SHIFT ori a0, a0, THREAD_CLF_ABORT sw a0, THREAD_CORE_LOCAL_FLAGS(tp) /* * Save state on stack */ /* Load abt_stack_va_end and set it as sp */ load_xregs tp, THREAD_CORE_LOCAL_ABT_STACK_VA_END, REG_SP /* Now sp is abort sp, create stack for struct thread_abort_regs */ addi sp, sp, -THREAD_ABT_REGS_SIZE /* Save user sp */ load_xregs tp, THREAD_CORE_LOCAL_X0, REG_A0 store_xregs sp, THREAD_ABT_REG_SP, REG_A0 /* Restore user a0, a1 which can be saved later */ load_xregs tp, THREAD_CORE_LOCAL_X1, REG_A0, REG_A1 /* Save user gp */ store_xregs sp, THREAD_ABT_REG_GP, REG_GP /* * Set the scratch register to 0 such in case of a recursive * exception thread_trap_vect() knows that it is emitted from kernel. */ csrrw gp, CSR_XSCRATCH, zero /* Save user tp we previously swapped into CSR_XSCRATCH */ store_xregs sp, THREAD_ABT_REG_TP, REG_GP /* Set kernel gp */ .option push .option norelax la gp, __global_pointer$ .option pop /* Save all other GPRs */ store_xregs sp, THREAD_ABT_REG_RA, REG_RA store_xregs sp, THREAD_ABT_REG_T0, REG_T0, REG_T2 store_xregs sp, THREAD_ABT_REG_S0, REG_S0, REG_S1 store_xregs sp, THREAD_ABT_REG_A0, REG_A0, REG_A7 store_xregs sp, THREAD_ABT_REG_S2, REG_S2, REG_S11 store_xregs sp, THREAD_ABT_REG_T3, REG_T3, REG_T6 /* Save XIE */ csrr t0, CSR_XIE store_xregs sp, THREAD_ABT_REG_IE, REG_T0 /* Mask all interrupts */ csrw CSR_XIE, x0 /* Save XSTATUS */ csrr t0, CSR_XSTATUS store_xregs sp, THREAD_ABT_REG_STATUS, REG_T0 /* Save XEPC */ csrr t0, CSR_XEPC store_xregs sp, THREAD_ABT_REG_EPC, REG_T0 /* Save XTVAL */ csrr t0, CSR_XTVAL store_xregs sp, THREAD_ABT_REG_TVAL, REG_T0 /* Save XCAUSE */ csrr a0, CSR_XCAUSE store_xregs sp, THREAD_ABT_REG_CAUSE, REG_A0 /* * a0 = cause * a1 = sp (struct thread_abort_regs *regs) * Call abort_handler(cause, regs) */ mv a1, sp call abort_handler /* * Restore state from stack */ /* Restore XEPC */ load_xregs sp, THREAD_ABT_REG_EPC, REG_T0 csrw CSR_XEPC, t0 /* Restore XIE */ load_xregs sp, THREAD_ABT_REG_IE, REG_T0 csrw CSR_XIE, t0 /* Restore XSTATUS */ load_xregs sp, THREAD_ABT_REG_STATUS, REG_T0 csrw CSR_XSTATUS, t0 /* Set scratch as thread_core_local */ csrw CSR_XSCRATCH, tp /* Update core local flags */ lw a0, THREAD_CORE_LOCAL_FLAGS(tp) srli a0, a0, THREAD_CLF_SAVED_SHIFT sw a0, THREAD_CORE_LOCAL_FLAGS(tp) /* Restore all GPRs */ load_xregs sp, THREAD_ABT_REG_RA, REG_RA load_xregs sp, THREAD_ABT_REG_GP, REG_GP load_xregs sp, THREAD_ABT_REG_TP, REG_TP load_xregs sp, THREAD_ABT_REG_T0, REG_T0, REG_T2 load_xregs sp, THREAD_ABT_REG_S0, REG_S0, REG_S1 load_xregs sp, THREAD_ABT_REG_A0, REG_A0, REG_A7 load_xregs sp, THREAD_ABT_REG_S2, REG_S2, REG_S11 load_xregs sp, THREAD_ABT_REG_T3, REG_T3, REG_T6 load_xregs sp, THREAD_ABT_REG_SP, REG_SP XRET END_FUNC trap_from_user 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // core/arch/riscv/kernel/thread_arch.c void thread_interrupt_handler(unsigned long cause, struct thread_ctx_regs *regs) { switch (cause & LONG_MAX) { case IRQ_XTIMER: [thread_foreign_interrupt_handler](/posts/optee-interrupts/)(regs); break; case IRQ_XSOFT: [thread_foreign_interrupt_handler](/posts/optee-interrupts/)(regs); break; case IRQ_XEXT: [thread_foreign_interrupt_handler](/posts/optee-interrupts/)(regs); break; default: thread_unhandled_trap(cause, regs); } } 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 // core/arch/riscv/kernel/thread_rv.S /* * void thread_foreign_interrupt_handler(struct thread_ctx_regs *regs) */ FUNC thread_foreign_interrupt_handler , : /* Mask all interrupt. */ csrw CSR_XIE, x0 mv s0, a0 /* tp = struct thread_core_local */ /* * Update core local flags */ lw s2, THREAD_CORE_LOCAL_FLAGS(tp) slli s2, s2, THREAD_CLF_SAVED_SHIFT ori s2, s2, THREAD_CLF_TMP ori s2, s2, THREAD_CLF_FIQ sw s2, THREAD_CORE_LOCAL_FLAGS(tp) /* * Mark current thread as suspended. * a0 = THREAD_FLAGS_EXIT_ON_FOREIGN_INTR * a1 = status * a2 = epc * thread_state_suspend(flags, status, pc) */ li a0, THREAD_FLAGS_EXIT_ON_FOREIGN_INTR LDR a1, THREAD_CTX_REG_STATUS(s0) LDR a2, THREAD_CTX_REG_EPC(s0) call [thread_state_suspend](/posts/optee-threads/) /* Now return value a0 contains suspended thread ID. */ /* Update core local flags */ lw s3, THREAD_CORE_LOCAL_FLAGS(tp) srli s3, s3, THREAD_CLF_SAVED_SHIFT ori s3, s3, THREAD_CLF_TMP sw s3, THREAD_CORE_LOCAL_FLAGS(tp) /* Passing thread index in a0, and prepare to return to REE. */ mv a4, a0 li a0, TEEABI_OPTEED_RETURN_CALL_DONE li a1, OPTEE_ABI_RETURN_RPC_FOREIGN_INTR mv a2, zero mv a3, zero mv a5, zero j [thread_return_to_udomain](/posts/optee-threads/) END_FUNC thread_foreign_interrupt_handler 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 // core/arch/riscv/kernel/thread_arch.c void thread_scall_handler(struct thread_scall_regs *regs) { struct ts_session *sess = NULL; uint32_t state = 0; /* Enable native interrupts */ state = thread_get_exceptions(); thread_unmask_exceptions(state & ~THREAD_EXCP_NATIVE_INTR); // Do nothing in RISC-V. thread_user_save_vfp(); sess = ts_get_current_session(); /* Restore foreign interrupts which are disabled on exception entry */ thread_restore_foreign_intr(); assert(sess && sess->handle_scall); if (!sess->handle_scall(regs)) { setup_unwind_user_mode(regs); [thread_exit_user_mode](/posts/optee-interrupts/)(regs->a0, regs->a1, regs->a2, regs->a3, regs->sp, regs->ra, regs->status); } } 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 // core/arch/riscv/kernel/thread_rv.S /* * void thread_exit_user_mode(unsigned long a0, unsigned long a1, * unsigned long a2, unsigned long a3, * unsigned long sp, unsigned long pc, * unsigned long status); */ FUNC thread_exit_user_mode , : /* Set kernel stack pointer */ mv sp, a4 /* Set xSTATUS */ csrw CSR_XSTATUS, a6 /* * Zeroize xSCRATCH to indicate to thread_trap_vect() * that we are executing in kernel. */ csrw CSR_XSCRATCH, zero /* * Mask all interrupts first. Interrupts will be unmasked after * returning from __thread_enter_user_mode(). */ csrw CSR_XIE, zero /* Set epc as thread_unwind_user_mode() */ csrw CSR_XEPC, a5 XRET END_FUNC thread_exit_user_mode

2024/10/11 · 14 分鐘 · 2864 字 · Frank Chang

OP-TEE: Memory Management

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca 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 // core/arch/riscv/include/mm/generic_ram_layout.h /* * Generic RAM layout configuration directives * * Mandatory directives: * CFG_TDDRAM_START * CFG_TDDRAM_SIZE * CFG_SHMEM_START * CFG_SHMEM_SIZE * * Optional directives: * CFG_TEE_LOAD_ADDR If defined sets TEE_LOAD_ADDR. If not, TEE_LOAD_ADDR * is set by the platform or defaults to TEE_RAM_START. * CFG_TEE_RAM_VA_SIZE Some platforms may have specific needs * * Optional directives when pager is enabled: * CFG_TDSRAM_START If no set, emulated at CFG_TDDRAM_START * CFG_TDSRAM_SIZE Default to CFG_CORE_TDSRAM_EMUL_SIZE * * Optional directive when CFG_SECURE_DATA_PATH is enabled: * CFG_TEE_SDP_MEM_SIZE If CFG_TEE_SDP_MEM_BASE is not defined, SDP test * memory byte size can be set by CFG_TEE_SDP_MEM_SIZE. * * This header file produces the following generic macros upon the mandatory * and optional configuration directives listed above: * * TEE_RAM_START TEE core RAM physical base address * TEE_RAM_VA_SIZE TEE core virtual memory address range size * TEE_RAM_PH_SIZE TEE core physical RAM byte size * TA_RAM_START TA contexts/pagestore RAM physical base address * TA_RAM_SIZE TA contexts/pagestore RAM byte size * TEE_SHMEM_START Non-secure static shared memory physical base address * TEE_SHMEM_SIZE Non-secure static shared memory byte size * * TDDRAM_BASE Main/external secure RAM base address * TDDRAM_SIZE Main/external secure RAM byte size * TDSRAM_BASE On-chip secure RAM base address, required by pager. * TDSRAM_SIZE On-chip secure RAM byte size, required by pager. * * TEE_LOAD_ADDR Only defined here if CFG_TEE_LOAD_ADDR is defined. * Otherwise we expect the platform_config.h to define it * unless which LEE_LOAD_ADDR defaults to TEE_RAM_START. * * TEE_RAM_VA_SIZE Set to CFG_TEE_RAM_VA_SIZE or defaults to * CORE_MMU_PGDIR_SIZE. * * TEE_SDP_TEST_MEM_BASE Define if a SDP memory pool is required and none set. * Always defined in the inner top (high addresses) * of CFG_TDDRAM_START/_SIZE. * TEE_SDP_TEST_MEM_SIZE Set to CFG_TEE_SDP_MEM_SIZE or a default size. * * ---------------------------------------------------------------------------- * TEE RAM layout without CFG_WITH_PAGER *_ * +----------------------------------+ <-- CFG_TDDRAM_START * | TEE core secure RAM (TEE_RAM) | * +----------------------------------+ * | Trusted Application RAM (TA_RAM) | * +----------------------------------+ * | SDP test memory (optional) | * +----------------------------------+ <-- CFG_TDDRAM_START + CFG_TDDRAM_SIZE * * +----------------------------------+ <-- CFG_SHMEM_START * | Non-secure static SHM | * +----------------------------------+ <-- CFG_SHMEM_START + CFG_SHMEM_SIZE * * ---------------------------------------------------------------------------- * TEE RAM layout with CFG_WITH_PAGER=y and undefined CFG_TDSRAM_START/_SIZE * * +----------------------------------+ <-- CFG_TDDRAM_START * | TEE core secure RAM (TEE_RAM) | | | CFG_CORE_TDSRAM_EMUL_SIZE * +----------------------------------+ --|-' * | reserved (for kasan) | | TEE_RAM_VA_SIZE * +----------------------------------+ --' * | TA RAM / Pagestore (TA_RAM) | * +----------------------------------+ <---- align with CORE_MMU_PGDIR_SIZE * +----------------------------------+ <-- * | SDP test memory (optional) | | CFG_TEE_SDP_MEM_SIZE * +----------------------------------+ <-+ CFG_TDDRAM_START + CFG_TDDRAM_SIZE * * +----------------------------------+ <-- CFG_SHMEM_START * | Non-secure static SHM | | * +----------------------------------+ v CFG_SHMEM_SIZE * * ---------------------------------------------------------------------------- * TEE RAM layout with CFG_WITH_PAGER=y and define CFG_TDSRAM_START/_SIZE * * +----------------------------------+ <-- CFG_TDSRAM_START * | TEE core secure RAM (TEE_RAM) | | CFG_TDSRAM_SIZE * +----------------------------------+ --' * * +----------------------------------+ <- CFG_TDDRAM_START * | TA RAM / Pagestore (TA_RAM) | * |----------------------------------+ <---- align with CORE_MMU_PGDIR_SIZE * |----------------------------------+ <-- * | SDP test memory (optional) | | CFG_TEE_SDP_MEM_SIZE * +----------------------------------+ <-+ CFG_TDDRAM_START + CFG_TDDRAM_SIZE * * +----------------------------------+ <-- CFG_SHMEM_START * | Non-secure static SHM | | * +----------------------------------+ v CFG_SHMEM_SIZE */ 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 // core/arch/riscv/mm/core_mmu_arch.c static struct mmu_pgt root_pgt[CFG_TEE_CORE_NB_CORE] __aligned(RISCV_PGSIZE) __section(".nozi.mmu.root_pgt"); static struct mmu_pgt pool_pgts[RISCV_MMU_MAX_PGTS] __aligned(RISCV_PGSIZE) __section(".nozi.mmu.pool_pgts"); static struct mmu_pgt user_pgts[CFG_NUM_THREADS] __aligned(RISCV_PGSIZE) __section(".nozi.mmu.usr_pgts"); struct mmu_partition { struct mmu_pgt *root_pgt; struct mmu_pgt *pool_pgts; struct mmu_pgt *user_pgts; unsigned int pgts_used; unsigned int asid; }; static struct mmu_partition default_partition __nex_data = { .root_pgt = root_pgt, // Root page tables, per-core. .pool_pgts = pool_pgts, // Page tables pool. .user_pgts = user_pgts, // User page tables, per-thread. .pgts_used = 0, // Increased when a pool page table is allocated. // See: core_mmu_pgt_alloc(). .asid = 0 }; 1 2 3 4 5 6 7 8 9 10 // include/mm/tee_mmu_types.h struct tee_mmap_region { unsigned int type; /* enum teecore_memtypes */ unsigned int region_size; paddr_t pa; vaddr_t va; size_t size; uint32_t attr; /* TEE_MATTR_* above */ }; 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 // include/mm/core_mmu.h /* * Memory area type: * MEM_AREA_END: Reserved, marks the end of a table of mapping areas. * MEM_AREA_TEE_RAM: core RAM (read/write/executable, secure, reserved to TEE) * MEM_AREA_TEE_RAM_RX: core private read-only/executable memory (secure) * MEM_AREA_TEE_RAM_RO: core private read-only/non-executable memory (secure) * MEM_AREA_TEE_RAM_RW: core private read/write/non-executable memory (secure) * MEM_AREA_INIT_RAM_RO: init private read-only/non-executable memory (secure) * MEM_AREA_INIT_RAM_RX: init private read-only/executable memory (secure) * MEM_AREA_NEX_RAM_RO: nexus private read-only/non-executable memory (secure) * MEM_AREA_NEX_RAM_RW: nexus private r/w/non-executable memory (secure) * MEM_AREA_TEE_COHERENT: teecore coherent RAM (secure, reserved to TEE) * MEM_AREA_TEE_ASAN: core address sanitizer RAM (secure, reserved to TEE) * MEM_AREA_IDENTITY_MAP_RX: core identity mapped r/o executable memory (secure) * MEM_AREA_TA_RAM: Secure RAM where teecore loads/exec TA instances. * MEM_AREA_NSEC_SHM: NonSecure shared RAM between NSec and TEE. * MEM_AREA_NEX_NSEC_SHM: nexus non-secure shared RAM between NSec and TEE. * MEM_AREA_RAM_NSEC: NonSecure RAM storing data * MEM_AREA_RAM_SEC: Secure RAM storing some secrets * MEM_AREA_ROM_SEC: Secure read only memory storing some secrets * MEM_AREA_IO_NSEC: NonSecure HW mapped registers * MEM_AREA_IO_SEC: Secure HW mapped registers * MEM_AREA_EXT_DT: Memory loads external device tree * MEM_AREA_MANIFEST_DT: Memory loads manifest device tree * MEM_AREA_TRANSFER_LIST: Memory area mapped for Transfer List * MEM_AREA_RES_VASPACE: Reserved virtual memory space * MEM_AREA_SHM_VASPACE: Virtual memory space for dynamic shared memory buffers * MEM_AREA_TS_VASPACE: TS va space, only used with phys_to_virt() * MEM_AREA_DDR_OVERALL: Overall DDR address range, candidate to dynamic shm. * MEM_AREA_SEC_RAM_OVERALL: Whole secure RAM * MEM_AREA_MAXTYPE: lower invalid 'type' value */ enum teecore_memtypes { MEM_AREA_END = 0, MEM_AREA_TEE_RAM, MEM_AREA_TEE_RAM_RX, MEM_AREA_TEE_RAM_RO, MEM_AREA_TEE_RAM_RW, MEM_AREA_INIT_RAM_RO, MEM_AREA_INIT_RAM_RX, MEM_AREA_NEX_RAM_RO, MEM_AREA_NEX_RAM_RW, MEM_AREA_TEE_COHERENT, MEM_AREA_TEE_ASAN, MEM_AREA_IDENTITY_MAP_RX, MEM_AREA_TA_RAM, MEM_AREA_NSEC_SHM, MEM_AREA_NEX_NSEC_SHM, MEM_AREA_RAM_NSEC, MEM_AREA_RAM_SEC, MEM_AREA_ROM_SEC, MEM_AREA_IO_NSEC, MEM_AREA_IO_SEC, MEM_AREA_EXT_DT, MEM_AREA_MANIFEST_DT, MEM_AREA_TRANSFER_LIST, MEM_AREA_RES_VASPACE, MEM_AREA_SHM_VASPACE, MEM_AREA_TS_VASPACE, MEM_AREA_PAGER_VASPACE, MEM_AREA_SDP_MEM, MEM_AREA_DDR_OVERALL, MEM_AREA_SEC_RAM_OVERALL, MEM_AREA_MAXTYPE }; 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 // mm/core_mmu.c /* Define the platform's memory layout. */ struct memaccess_area { paddr_t paddr; size_t size; }; #define MEMACCESS_AREA(a, s) { .paddr = a, .size = s } // secure_only[] defines the bases and the sizes of secure memories // used by OP-TEE. static struct memaccess_area secure_only[] __nex_data = { #ifdef CFG_CORE_PHYS_RELOCATABLE MEMACCESS_AREA(0, 0), #else #ifdef TRUSTED_SRAM_BASE MEMACCESS_AREA(TRUSTED_SRAM_BASE, TRUSTED_SRAM_SIZE), #endif // e.g. // TRUSTED_DRAM_BASE = TDDRAM_BASE = CFG_TDDRAM_START = 0xf1000000 // TRUSTED_DRAM_SIZE = TDDRAM_SIZE = CFG_TDDRAM_SIZE = 0x01000000 (16 MB) MEMACCESS_AREA(TRUSTED_DRAM_BASE, TRUSTED_DRAM_SIZE), #endif }; // nsec_shared[] defines the bases and sizes of the non-secure static // shared memories. static struct memaccess_area nsec_shared[] __nex_data = { #ifdef CFG_CORE_RESERVED_SHM // e.g. // TEE_SHMEM_START = TEE_SHMEM_START = CFG_SHMEM_START // TEE_SHMEM_SIZE = TEE_SHMEM_SIZE = CFG_SHMEM_SIZE MEMACCESS_AREA(TEE_SHMEM_START, TEE_SHMEM_SIZE), #endif }; 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 // mm/core_mmu.c static struct tee_mmap_region static_memory_map[CFG_MMAP_REGIONS #if defined(CFG_CORE_ASLR) || defined(CFG_CORE_PHYS_RELOCATABLE) + 1 #endif + 1] __nex_bss; ..... // _start() -> core_init_mmu_map() /* * core_init_mmu_map() - init tee core default memory mapping * * This routine sets the static default TEE core mapping. If @seed is > 0 * and configured with CFG_CORE_ASLR it will map tee core at a location * based on the seed and return the offset from the link address. * * If an error happened: core_init_mmu_map is expected to panic. * * Note: this function is weak just to make it possible to exclude it from * the unpaged area. */ void __weak core_init_mmu_map(unsigned long seed, struct core_mmu_config *cfg) { #ifndef CFG_NS_VIRTUALIZATION vaddr_t start = ROUNDDOWN((vaddr_t)__nozi_start, SMALL_PAGE_SIZE); #else vaddr_t start = ROUNDDOWN((vaddr_t)__vcore_nex_rw_start, SMALL_PAGE_SIZE); #endif vaddr_t len = ROUNDUP((vaddr_t)__nozi_end, SMALL_PAGE_SIZE) - start; // tmp_mmap is allocated from the heap (__heap1_start or __heap2_start). struct tee_mmap_region *tmp_mmap = get_tmp_mmap(); unsigned long offs = 0; if (IS_ENABLED(CFG_CORE_PHYS_RELOCATABLE) && (core_mmu_tee_load_pa & SMALL_PAGE_MASK)) panic("OP-TEE load address is not page aligned"); check_sec_nsec_mem_config(); /* * Add a entry covering the translation tables which will be * involved in some virt_to_phys() and phys_to_virt() conversions. */ static_memory_map[0] = (struct tee_mmap_region){ .type = MEM_AREA_TEE_RAM, .region_size = SMALL_PAGE_SIZE, .pa = start, .va = start, .size = len, .attr = core_mmu_type_to_attr(MEM_AREA_IDENTITY_MAP_RX), }; COMPILE_TIME_ASSERT(CFG_MMAP_REGIONS >= 13); // Initalize memory maps. offs = init_mem_map(tmp_mmap, ARRAY_SIZE(static_memory_map), seed); check_mem_map(tmp_mmap); // Set page table entries for memory maps. [core_init_mmu(tmp_mmap);](/posts/optee-memory-mgnt/) dump_xlat_table(0x0, CORE_MMU_BASE_TABLE_LEVEL); // Set cfg->satp[]. core_init_mmu_regs(cfg); cfg->map_offset = offs; // Copy the temp memory maps. memcpy(static_memory_map, tmp_mmap, sizeof(static_memory_map)); } 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 // mm/core_mmu.c // _start() -> core_init_mmu_map() -> init_mem_map() -> collect_mem_ranges() // e.g. // D/TC:0 add_phys_mem:677 VCORE_UNPG_RX_PA type TEE_RAM_RX 0xf1000000 size 0x00092000 // D/TC:0 add_phys_mem:677 VCORE_UNPG_RW_PA type TEE_RAM_RW 0xf1092000 size 0x0016e000 // D/TC:0 add_phys_mem:677 ta_base type TA_RAM 0xf1200000 size 0x00e00000 // D/TC:0 add_va_space:717 type RES_VASPACE size 0x00a00000 // D/TC:0 add_va_space:717 type SHM_VASPACE size 0x02000000 // D/TC:0 dump_mmap_table:849 type TEE_RAM_RX va 0xf1000000..0xf1091fff pa 0xf1000000..0xf1091fff size 0x00092000 (smallpg) // D/TC:0 dump_mmap_table:849 type TEE_RAM_RW va 0xf1092000..0xf11fffff pa 0xf1092000..0xf11fffff size 0x0016e000 (smallpg) // D/TC:0 dump_mmap_table:849 type RES_VASPACE va 0xf1200000..0xf1bfffff pa 0x00000000..0x009fffff size 0x00a00000 (pgdir) // D/TC:0 dump_mmap_table:849 type SHM_VASPACE va 0xf1c00000..0xf3bfffff pa 0x00000000..0x01ffffff size 0x02000000 (pgdir) // D/TC:0 dump_mmap_table:849 type TA_RAM va 0xf3c00000..0xf49fffff pa 0xf1200000..0xf1ffffff size 0x00e00000 (pgdir) static size_t collect_mem_ranges(struct tee_mmap_region *memory_map, size_t num_elems) { const struct core_mmu_phys_mem *mem = NULL; vaddr_t ram_start = secure_only[0].paddr; size_t last = 0; #define ADD_PHYS_MEM(_type, _addr, _size) \ add_phys_mem(memory_map, num_elems, #_addr, (_type), \ (_addr), (_size), &last) if (IS_ENABLED(CFG_CORE_RWDATA_NOEXEC)) { ADD_PHYS_MEM(MEM_AREA_TEE_RAM_RO, ram_start, VCORE_UNPG_RX_PA - ram_start); ADD_PHYS_MEM(MEM_AREA_TEE_RAM_RX, VCORE_UNPG_RX_PA, VCORE_UNPG_RX_SZ); ADD_PHYS_MEM(MEM_AREA_TEE_RAM_RO, VCORE_UNPG_RO_PA, VCORE_UNPG_RO_SZ); if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) { ADD_PHYS_MEM(MEM_AREA_NEX_RAM_RO, VCORE_UNPG_RW_PA, VCORE_UNPG_RW_SZ); ADD_PHYS_MEM(MEM_AREA_NEX_RAM_RW, VCORE_NEX_RW_PA, VCORE_NEX_RW_SZ); } else { ADD_PHYS_MEM(MEM_AREA_TEE_RAM_RW, VCORE_UNPG_RW_PA, VCORE_UNPG_RW_SZ); } if (IS_ENABLED(CFG_WITH_PAGER)) { ADD_PHYS_MEM(MEM_AREA_INIT_RAM_RX, VCORE_INIT_RX_PA, VCORE_INIT_RX_SZ); ADD_PHYS_MEM(MEM_AREA_INIT_RAM_RO, VCORE_INIT_RO_PA, VCORE_INIT_RO_SZ); } } else { ADD_PHYS_MEM(MEM_AREA_TEE_RAM, TEE_RAM_START, TEE_RAM_PH_SIZE); } if (IS_ENABLED(CFG_NS_VIRTUALIZATION)) { ADD_PHYS_MEM(MEM_AREA_SEC_RAM_OVERALL, TRUSTED_DRAM_BASE, TRUSTED_DRAM_SIZE); } else { /* * Every guest will have own TA RAM if virtualization * support is enabled. */ paddr_t ta_base = 0; size_t ta_size = 0; core_mmu_get_ta_range(&ta_base, &ta_size); ADD_PHYS_MEM(MEM_AREA_TA_RAM, ta_base, ta_size); } if (IS_ENABLED(CFG_CORE_SANITIZE_KADDRESS) && IS_ENABLED(CFG_WITH_PAGER)) { /* * Asan ram is part of MEM_AREA_TEE_RAM_RW when pager is * disabled. */ ADD_PHYS_MEM(MEM_AREA_TEE_ASAN, ASAN_MAP_PA, ASAN_MAP_SZ); } #undef ADD_PHYS_MEM /* Collect device memory info from SP manifest */ if (IS_ENABLED(CFG_CORE_SEL2_SPMC)) collect_device_mem_ranges(memory_map, num_elems, &last); for (mem = phys_mem_map_begin; mem < phys_mem_map_end; mem++) { /* Only unmapped virtual range may have a null phys addr */ assert(mem->addr || !core_mmu_type_to_attr(mem->type)); add_phys_mem(memory_map, num_elems, mem->name, mem->type, mem->addr, mem->size, &last); } if (IS_ENABLED(CFG_SECURE_DATA_PATH)) verify_special_mem_areas(memory_map, phys_sdp_mem_begin, phys_sdp_mem_end, "SDP"); add_va_space(memory_map, num_elems, MEM_AREA_RES_VASPACE, CFG_RESERVED_VASPACE_SIZE, &last); add_va_space(memory_map, num_elems, MEM_AREA_SHM_VASPACE, SHM_VASPACE_SIZE, &last); memory_map[last].type = MEM_AREA_END; return last; } 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 // mm/core_mmu.c static void add_phys_mem(struct tee_mmap_region *memory_map, size_t num_elems, const char *mem_name __maybe_unused, enum teecore_memtypes mem_type, paddr_t mem_addr, paddr_size_t mem_size, size_t *last) { size_t n = 0; paddr_t pa; paddr_size_t size; if (!mem_size) /* Discard null size entries */ return; /* * If some ranges of memory of the same type do overlap * each others they are coalesced into one entry. To help this * added entries are sorted by increasing physical. * * Note that it's valid to have the same physical memory as several * different memory types, for instance the same device memory * mapped as both secure and non-secure. This will probably not * happen often in practice. */ DMSG("%s type %s 0x%08" PRIxPA " size 0x%08" PRIxPASZ, mem_name, teecore_memtype_name(mem_type), mem_addr, mem_size); // Iternate the existing memory maps. // If there's any existing memory region overlap with the one // we intend to add, if both of their memory types are same, // merge them into a single memory region. // After the iteration, 'n' will be the position to be added into memory maps. while (true) { if (n >= (num_elems - 1)) { EMSG("Out of entries (%zu) in memory_map", num_elems); panic(); } if (n == *last) break; pa = memory_map[n].pa; size = memory_map[n].size; // Merge the overlapped memory regions. if (mem_type == memory_map[n].type && ((pa <= (mem_addr + (mem_size - 1))) && (mem_addr <= (pa + (size - 1))))) { DMSG("Physical mem map overlaps 0x%" PRIxPA, mem_addr); memory_map[n].pa = MIN(pa, mem_addr); memory_map[n].size = MAX(size, mem_size) + (pa - memory_map[n].pa); return; } if (mem_type < memory_map[n].type || (mem_type == memory_map[n].type && mem_addr < pa)) break; /* found the spot where to insert this memory */ n++; } // Insert the memory region into the memory maps. memmove(memory_map + n + 1, memory_map + n, sizeof(struct tee_mmap_region) * (*last - n)); (*last)++; memset(memory_map + n, 0, sizeof(memory_map[0])); memory_map[n].type = mem_type; memory_map[n].pa = mem_addr; memory_map[n].size = mem_size; } 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 // mm/core_mmu.c static void add_va_space(struct tee_mmap_region *memory_map, size_t num_elems, enum teecore_memtypes type, size_t size, size_t *last) { size_t n = 0; DMSG("type %s size 0x%08zx", teecore_memtype_name(type), size); // Find the position to insert the memory region into the memory maps. while (true) { if (n >= (num_elems - 1)) { EMSG("Out of entries (%zu) in memory_map", num_elems); panic(); } if (n == *last) break; if (type < memory_map[n].type) break; n++; } // Insert the memory region into the memory maps without physical address. memmove(memory_map + n + 1, memory_map + n, sizeof(struct tee_mmap_region) * (*last - n)); (*last)++; memset(memory_map + n, 0, sizeof(memory_map[0])); memory_map[n].type = type; memory_map[n].size = size; } 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 // core/arch/riscv/mm/core_mmu_arch.c // _start() -> reset_primary() -> core_init_mmu_map() -> core_init_mmu() // The memory maps are created in init_mem_map(). // core_init_mmu() will set the page table entries for memory maps // on the root page table (root_pgt) by calling: // core_init_mmu_prtn_tee() -> core_mmu_map_region(). // Page table entries are also copied to the root page tables of the // rest of the cores in core_init_mmu_prtn_tee(). void core_init_mmu(struct tee_mmap_region *mm) { uint64_t max_va = 0; size_t n = 0; static_assert((RISCV_MMU_MAX_PGTS * RISCV_MMU_PGT_SIZE) == sizeof(pool_pgts)); /* Initialize default pagetables */ core_init_mmu_prtn_tee(&default_partition, mm); for (n = 0; !core_mmap_is_end_of_table(mm + n); n++) { vaddr_t va_end = mm[n].va + mm[n].size - 1; if (va_end > max_va) max_va = va_end; } set_user_va_idx(&default_partition); core_init_mmu_prtn_ta(&default_partition); assert(max_va < BIT64(RISCV_MMU_VA_WIDTH)); } 1 2 3 4 5 // core/arch/riscv/kernel/entry.S LOCAL_DATA boot_mmu_config , : /* struct core_mmu_config */ .skip CORE_MMU_CONFIG_SIZE END_DATA boot_mmu_config 1 2 3 4 5 6 7 8 9 10 // core/arch/riscv/mm/core_mmu_arch.h struct core_mmu_config { // satp[] is set to root page table (root_pgt) for each core by: // _start() -> reset_primary() -> core_init_mmu_map() -> // core_init_mmu_regs(). // And is set to satp CSR in: set_satp() for each core. unsigned long satp[CFG_TEE_CORE_NB_CORE]; uint32_t map_offset; }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // core/include/mm/tee_mm.h struct _tee_mm_entry_t { struct _tee_mm_pool_t *pool; struct _tee_mm_entry_t *next; uint32_t offset; /* offset in pages/sections */ uint32_t size; /* size in pages/sections */ }; typedef struct _tee_mm_entry_t tee_mm_entry_t; struct _tee_mm_pool_t { tee_mm_entry_t *entry; paddr_t lo; /* low boundary of the pool */ paddr_size_t size; /* pool size */ uint32_t flags; /* Config flags for the pool */ uint8_t shift; /* size shift */ unsigned int lock; #ifdef CFG_WITH_STATS size_t max_allocated; #endif }; typedef struct _tee_mm_pool_t tee_mm_pool_t; 1 2 3 4 5 6 7 8 9 10 // core/mm/core_mmu.c /* Physical Secure DDR pool */ tee_mm_pool_t tee_mm_sec_ddr; /* Virtual memory pool for core mappings */ tee_mm_pool_t core_virt_mem_pool; /* Virtual memory pool for shared memory mappings */ tee_mm_pool_t core_virt_shm_pool;

2024/10/08 · 14 分鐘 · 2936 字 · Frank Chang

OP-TEE: Initialization

⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca _start() Run a lottery to decide the primary hart. Use amoadd.w to decide which core is the primary hart. For primary hart: reset_primary() For secondary hart: reset_secondary() reset_primary() Zero .bss section. set_tp() Set $tp to thread_core_local[hartid]. Save current hart ID to thread_core_local[hartid].hart_id. thread_init_thread_core_local() Set thread_core_local.curr_thread to THREAD_ID_INVALID for all cores (CFG_TEE_CORE_NB_CORE). Set thread_core_local.flag to THREAD_CLF_TMP to indicate that it’s using the temporary stack for all cores (CFG_TEE_CORE_NB_CORE). Set first core’s thread_core_local[0].tmp_stack_va_end to stack_tmp[0]. plat_primary_init_early() ...

2024/10/06 · 6 分鐘 · 1141 字 · Frank Chang

QEMU: 使用 Decodetree 新增 RISC-V 指令

⚠️ 本文所使用的 QEMU 版本為:v4.2.0 在之前的文章中 (Part 1., Part 2.) 我們提到了如何使用 Decodetree 來定義指令的 decoder。本篇文章就實際使用 Decodetree 來定義一個 QEMU RISC-V 目前尚未支援的指令 - B(itmanip) Extension 中的 pcnt 指令,並實做其行為。 pcnt 指令 pcnt 指令的定義如下: This instruction counts the number of 1 bits in a register. This operations is known as population count, popcount, sideways sum, bit summation, or Hamming weight. 其指令格式為: 1 2 3 | 1 0 9 8 7 6 5 | 4 3 2 1 0 | 9 8 7 6 5 | 4 3 2 | 1 0 9 8 7 | 6 5 4 3 2 1 0 | |===========================================================================| | 0110000 | 00010 | rs1 | 001 | rd | 0010011 | PCNT 安裝 toolchain 由於 B Extension 尚未正式定稿 (Draft),因此必須至 riscv-bitmanip repo 下載 toolchain,並依照該 repo 的指示安裝: ...

2020/02/16 · 6 分鐘 · 1156 字 · Frank Chang

QEMU Decodetree 語法介紹 (Part 2.)

⚠️ 本文所使用的 QEMU 版本為:v4.2.0 延續 Part 1. 一文,本文將繼續介紹 Decodetree 中的 Patterns 及 Pattern Groups 語法。 Patterns Pattern 實際定義了一個指令的 decode 方式。Decodetree 會根據 Patterns 的定義,來動態產生出對應的 switch-case decode 判斷式。 1 2 3 4 pat_def := identifier ( pat_elt )+ pat_elt := fixedbit_elt | field_elt | field_ref | args_ref | fmt_ref | const_elt fmt_ref := '@' identifier const_elt := identifier '=' number 其語法由使用者所定義的 identifier,隨後緊接著一個以上的 pat_elt。 ...

2020/02/01 · 8 分鐘 · 1664 字 · Frank Chang

QEMU Decodetree 語法介紹 (Part 1.)

⚠️ 本文所使用的 QEMU 版本為:v4.2.0 QEMU 在 decode 指令的時候,需要呼叫各平台所定義的 instruction decoders 來解析指令。如在 ARM 平台下,就定義了:disas_arm_insn()、disas_thumb_insn() 及 disas_thumb2_insn() 等來分別負責 ARM 32-bits 指令、ARM Thumb 指令及 ARM Thumb2 指令的解析。 而 Decodetree 則是由 Bastian Koppelmann 於 2017 年在 porting RISC-V QEMU 的時候所提出來的機制 (詳見:討論串 1、討論串 2)。主因是過往的 instruction decoders (如:ARM) 都是採用一大包的 switch-case 來做判斷。不僅難閱讀,也難以維護。 因此 Bastian Koppelmann 就提出了 Decodetree 的機制,開發者只需要透過 Decodetree 的語法定義各個指令的格式,便可交由 Decodetree 來動態生成對應包含 switch-case 的 instruction decoder .c 檔。 Decodetree 特別適合像 RISC-V 這種具有固定指令格式的 ISA1。 因為各欄位都在固定的位置,(如 RISC-V 的 opcode 都是固定在 bits[6..0] 的位置),各指令可重複使用的定義相較於其他的 ISA 來得多。 ...

2020/01/31 · 8 分鐘 · 1664 字 · Frank Chang

Cross-build RISC-V QEMU

在 x86-64 上 cross-build RISC-V QEMU OS 為 Ubuntu 22.04 dpkg 加入 RISC-V 架構: sudo dpkg --add-architecture riscv64 新增 RISC-V package sources: sudo vim /etc/apt/sources.list 新增以下 RISC-V sources: 1 2 3 deb [arch=riscv64] http://ports.ubuntu.com jammy main restricted universe multiverse deb [arch=riscv64] http://ports.ubuntu.com jammy-updates main restricted universe multiverse deb [arch=riscv64] http://ports.ubuntu.com jammy-security main restricted universe multiverse 並限制以下的 sources 為 amd64 (加上:[arch=amd64]): ...

2020/01/26 · 2 分鐘 · 231 字 · Frank Chang

Build QEMU environments

Build QEMU 安裝所需的套件: sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex xinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev git libncurses5-dev libpixman-1-dev 下載 QEMU source codes: git clone --recursive [email protected]:qemu/qemu.git cd qemu ./configure --target-list=riscv64-softmmu,riscv32-softmmu,riscv64-linux-user,riscv32-linux-user make -j 幾個好用的 debug configure 參數: --extra-cflags=CFLAGS:append extra C compiler flags QEMU_CFLAGS e.g. --extra-cflags="-g3 --save-temps" --extra-ldflags=LDFLAGS:append extra linker flags LDFLAGS --enable-debug-tcg:enable TCG debugging --disable-debug-tcg:disable TCG debugging (default) --disable-debug-info:disable debugging information --enable-debug:enable common debug build options --disable-strip:disable stripping binaries --disable-werror:disable compilation abort on warning --enable-pie:build Position Independent Executables --disable-pie:do not build Position Independent Executables 幾個好用的執行參數: -d item1,...:enable logging of specified items ...

2020/01/25 · 7 分鐘 · 1477 字 · Frank Chang

Linux Kernel: BUILD_BUG_ON_ZERO() / BUILD_BUG_ON_NULL()

之前在 trace Linux Kernel source codes 時發現了兩個很特別的 macros:BUILD_BUG_ON_ZERO() 和 BUILD_BUG_ON_NULL() (定義在:include/linux/kernel.h) 它們的定義如下: 1 2 3 4 5 6 /* Force a compilation error if condition is true, but also produce a result (of value 0 and type size_t), so the expression can be used e.g. in a structure initializer (or where-ever else comma expressions aren't permitted). */ #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) #define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) 其中 e 是我們所傳入的判斷式,若判斷式為 true,則會造成 compile error。如此我們便可透過這個 macro 來判斷是否某些錯誤/不應發生的情況 (判斷式) 是否會發生,若會發生則可在 compile-time 的時候就顯示錯誤訊息。 ...

2012/10/14 · 3 分鐘 · 488 字 · Frank Chang

Linux Kernel: ARRAY_SIZE()

通常我們在 C 語言中取得陣列的元數個數可以透過下列的方式來計算: 1 2 3 4 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) int arr[10]; int arr_size = ARRAY_SIZE(arr); 但如同 Jserv 大大在 這篇文章 中所提到:ARRAY_SIZE() 這樣的 macro 其實是陷阱重重… 因為 macro 本身沒辦法做型態檢查,只是單純的將值帶入並展開,而在 C 中我們常常會將指標和陣列混著使用。因此若是我們將指向該陣列的指標傳入,就會得到錯誤的計算結果。 如下面的程式: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) int main(void) { int a[10]; int *a_ptr = a; printf("%d\n", ARRAY_SIZE(a)); printf("%d\n", ARRAY_SIZE(a_ptr)); return 0; } 若傳入陣列 a,則結果會正確顯示 size 大小為 10,但若傳入的是指向陣列 a 的指標 a_ptr,則因為指標的在 32 位元作業系統上大小為 4 bytes (4 / 4) 的結果則會變成 1,而並不是我們所要的答案 10。 ...

2012/10/13 · 3 分鐘 · 589 字 · Frank Chang