1 /*        $NetBSD: frame.h,v 1.48 2020/08/14 16:18:36 skrll Exp $     */
2 
3 /*
4  * Copyright (c) 1994-1997 Mark Brinicombe.
5  * Copyright (c) 1994 Brini.
6  * All rights reserved.
7  *
8  * This code is derived from software written for Brini by Mark Brinicombe
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by Brini.
21  * 4. The name of the company nor the name of the author may be used to
22  *    endorse or promote products derived from this software without specific
23  *    prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED
26  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28  * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  * RiscBSD kernel project
38  *
39  * frame.h
40  *
41  * Stack frames structures
42  *
43  * Created      : 30/09/94
44  */
45 
46 #ifndef _ARM32_FRAME_H_
47 #define _ARM32_FRAME_H_
48 
49 #include <arm/frame.h>                  /* Common ARM stack frames */
50 
51 #ifndef _LOCORE
52 
53 /*
54  * Switch frame.
55  *
56  * Should be a multiple of 8 bytes for dumpsys.
57  */
58 
59 struct switchframe {
60           u_int     sf_r4;
61           u_int     sf_r5;
62           u_int     sf_r6;
63           u_int     sf_r7;
64           u_int     sf_sp;
65           u_int     sf_pc;
66 };
67 
68 /*
69  * System stack frames.
70  */
71 
72 struct clockframe {
73           struct trapframe cf_tf;
74 };
75 
76 /*
77  * Stack frame. Used during stack traces (db_trace.c)
78  */
79 struct frame {
80           u_int     fr_fp;
81           u_int     fr_sp;
82           u_int     fr_lr;
83           u_int     fr_pc;
84 };
85 
86 #ifdef _KERNEL
87 void validate_trapframe(trapframe_t *, int);
88 #endif /* _KERNEL */
89 
90 #else /* _LOCORE */
91 
92 #include "opt_compat_netbsd.h"
93 #include "opt_execfmt.h"
94 #include "opt_multiprocessor.h"
95 #include "opt_cpuoptions.h"
96 #include "opt_arm_debug.h"
97 #include "opt_cputypes.h"
98 #include "opt_dtrace.h"
99 
100 #include <arm/locore.h>
101 
102 /*
103  * This macro is used by DO_AST_AND_RESTORE_ALIGNMENT_FAULTS to process
104  * any pending softints.
105  */
106 #ifdef _ARM_ARCH_4T
107 #define   B_CF_CONTROL(rX)                                                      ;\
108           ldr       ip, [rX, #CF_CONTROL]         /* get function addr */                 ;\
109           bx        ip                            /* branch to cpu_control */
110 #else
111 #define   B_CF_CONTROL(rX)                                                      ;\
112           ldr       pc, [rX, #CF_CONTROL]         /* branch to cpu_control */
113 #endif
114 #ifdef _ARM_ARCH_5T
115 #define   BL_CF_CONTROL(rX)                                                     ;\
116           ldr       ip, [rX, #CF_CONTROL]         /* get function addr */                 ;\
117           blx       ip                            /* call cpu_control */
118 #else
119 #define   BL_CF_CONTROL(rX)                                                     ;\
120           mov       lr, pc                                                                ;\
121           ldr       pc, [rX, #CF_CONTROL]         /* call cpu_control */
122 #endif
123 #if defined(__HAVE_FAST_SOFTINTS) && !defined(__HAVE_PIC_FAST_SOFTINTS)
124 #define   DO_PENDING_SOFTINTS                                                   \
125           ldr       r0, [r4, #CI_INTR_DEPTH]/* Get current intr depth */        ;\
126           cmp       r0, #0                        /* Test for 0. */             ;\
127           bne       10f                           /*   skip softints if != 0 */ ;\
128           ldr       r0, [r4, #CI_CPL]   /* Get current priority level */;\
129           ldr       r1, [r4, #CI_SOFTINTS]        /* Get pending softint mask */          ;\
130           lsrs      r0, r1, r0                    /* shift mask by cpl */                 ;\
131           blne      _C_LABEL(dosoftints)          /* dosoftints(void) */                  ;\
132 10:
133 #else
134 #define   DO_PENDING_SOFTINTS           /* nothing */
135 #endif
136 
137 #ifdef _ARM_ARCH_6
138 #define   GET_CPSR(rb)                            /* nothing */
139 #define   CPSID_I(ra,rb)                          cpsid     i
140 #define   CPSIE_I(ra,rb)                          cpsie     i
141 #else
142 #define   GET_CPSR(rb)                                                                    \
143           mrs       rb, cpsr            /* fetch CPSR */
144 
145 #define   CPSID_I(ra,rb)                                                                  \
146           orr       ra, rb, #(IF32_bits)                                                  ;\
147           msr       cpsr_c, ra                    /* Disable interrupts */
148 
149 #define   CPSIE_I(ra,rb)                                                                  \
150           bic       ra, rb, #(IF32_bits)                                                  ;\
151           msr       cpsr_c, ra                    /* Restore interrupts */
152 #endif
153 
154 #define DO_PENDING_AST(lbl)                                                     ;\
155 1:        ldr       r1, [r5, #L_MD_ASTPENDING] /* Pending AST? */               ;\
156           tst       r1, #1                                                                ;\
157           beq       lbl                           /* Nope. Just bail */                   ;\
158           bic       r0, r1, #1                     /* clear AST */              ;\
159           str       r0, [r5, #L_MD_ASTPENDING]                                  ;\
160           CPSIE_I(r6, r6)                         /* Restore interrupts */      ;\
161           mov       r0, sp                                                                ;\
162           bl        _C_LABEL(ast)                 /* ast(frame) */              ;\
163           CPSID_I(r0, r6)                         /* Disable interrupts */      ;\
164           b         1b                            /* test again */
165 
166 /*
167  * AST_ALIGNMENT_FAULT_LOCALS and ENABLE_ALIGNMENT_FAULTS
168  * These are used in order to support dynamic enabling/disabling of
169  * alignment faults when executing old a.out ARM binaries.
170  *
171  * Note that when ENABLE_ALIGNMENTS_FAULTS finishes r4 will contain
172  * curcpu() and r5 containing curlwp.  DO_AST_AND_RESTORE_ALIGNMENT_FAULTS
173  * relies on r4 and r5 being preserved.
174  */
175 #ifdef EXEC_AOUT
176 #define   AST_ALIGNMENT_FAULT_LOCALS                                            \
177 .Laflt_cpufuncs:                                                                ;\
178           .word     _C_LABEL(cpufuncs)
179 
180 /*
181  * This macro must be invoked following PUSHFRAMEINSVC or PUSHFRAME at
182  * the top of interrupt/exception handlers.
183  *
184  * When invoked, r0 *must* contain the value of SPSR on the current
185  * trap/interrupt frame. This is always the case if ENABLE_ALIGNMENT_FAULTS
186  * is invoked immediately after PUSHFRAMEINSVC or PUSHFRAME.
187  */
188 #define   ENABLE_ALIGNMENT_FAULTS                                                         \
189           and       r7, r0, #(PSR_MODE) /* Test for USR32 mode */     ;\
190           cmp       r7, #(PSR_USR32_MODE)                                                 ;\
191           GET_CURX(r4, r5)              /* r4 = curcpu, r5 = curlwp */          ;\
192           bne       1f                            /* Not USR mode skip AFLT */  ;\
193           ldr       r1, [r5, #L_MD_FLAGS]         /* Fetch l_md.md_flags */     ;\
194           tst       r1, #MDLWP_NOALIGNFLT                                                 ;\
195           beq       1f                            /* AFLTs already enabled */   ;\
196           ldr       r2, .Laflt_cpufuncs                                         ;\
197           ldr       r1, [r4, #CI_CTRL]  /* Fetch control register */  ;\
198           mov       r0, #-1                                                               ;\
199           BL_CF_CONTROL(r2)             /* Enable alignment faults */ ;\
200 1:        /* done */
201 
202 /*
203  * This macro must be invoked just before PULLFRAMEFROMSVCANDEXIT or
204  * PULLFRAME at the end of interrupt/exception handlers.  We know that
205  * r4 points to curcpu() and r5 points to curlwp since that is what
206  * ENABLE_ALIGNMENT_FAULTS did for us.
207  */
208 #define   DO_AST_AND_RESTORE_ALIGNMENT_FAULTS                                   \
209           DO_PENDING_SOFTINTS                                                   ;\
210           GET_CPSR(r6)                            /* save CPSR */                         ;\
211           CPSID_I(r1, r6)                         /* Disable interrupts */      ;\
212           cmp       r7, #(PSR_USR32_MODE)         /* Returning to USR mode? */  ;\
213           bne       3f                            /* Nope, get out now */                 ;\
214           DO_PENDING_AST(2f)            /* Pending AST? */            ;\
215 2:        ldr       r1, [r4, #CI_CURLWP]          /* get curlwp from cpu_info */          ;\
216           ldr       r0, [r1, #L_MD_FLAGS]         /* get md_flags from lwp */   ;\
217           tst       r0, #MDLWP_NOALIGNFLT                                                 ;\
218           beq       3f                            /* Keep AFLTs enabled */      ;\
219           ldr       r1, [r4, #CI_CTRL]  /* Fetch control register */  ;\
220           ldr       r2, .Laflt_cpufuncs                                         ;\
221           mov       r0, #-1                                                               ;\
222           bic       r1, r1, #CPU_CONTROL_AFLT_ENABLE  /* Disable AFLTs */       ;\
223           BL_CF_CONTROL(r2)             /* Set new CTRL reg value */  ;\
224 3:        /* done */
225 
226 #else     /* !EXEC_AOUT */
227 
228 #define   AST_ALIGNMENT_FAULT_LOCALS
229 
230 #define   ENABLE_ALIGNMENT_FAULTS                                                         \
231           and       r7, r0, #(PSR_MODE) /* Test for USR32 mode */     ;\
232           GET_CURX(r4, r5)              /* r4 = curcpu, r5 = curlwp */
233 
234 
235 #define   DO_AST_AND_RESTORE_ALIGNMENT_FAULTS                                   \
236           DO_PENDING_SOFTINTS                                                   ;\
237           GET_CPSR(r6)                            /* save CPSR */                         ;\
238           CPSID_I(r1, r6)                         /* Disable interrupts */      ;\
239           cmp       r7, #(PSR_USR32_MODE)                                                 ;\
240           bne       2f                            /* Nope, get out now */                 ;\
241           DO_PENDING_AST(2f)            /* Pending AST? */            ;\
242 2:        /* done */
243 #endif /* EXEC_AOUT */
244 
245 #ifndef _ARM_ARCH_6
246 #ifdef ARM_LOCK_CAS_DEBUG
247 #define   LOCK_CAS_DEBUG_LOCALS                                                            \
248 .L_lock_cas_restart:                                                                      ;\
249           .word     _C_LABEL(_lock_cas_restart)
250 
251 #if defined(__ARMEB__)
252 #define   LOCK_CAS_DEBUG_COUNT_RESTART                                           \
253           ble       99f                                                                   ;\
254           ldr       r0, .L_lock_cas_restart                                               ;\
255           ldmia     r0, {r1-r2}                   /* load ev_count */           ;\
256           adds      r2, r2, #1                    /* 64-bit incr (lo) */                  ;\
257           adc       r1, r1, #0                    /* 64-bit incr (hi) */                  ;\
258           stmia     r0, {r1-r2}                   /* store ev_count */
259 #else /* __ARMEB__ */
260 #define   LOCK_CAS_DEBUG_COUNT_RESTART                                           \
261           ble       99f                                                                   ;\
262           ldr       r0, .L_lock_cas_restart                                               ;\
263           ldmia     r0, {r1-r2}                   /* load ev_count */           ;\
264           adds      r1, r1, #1                    /* 64-bit incr (lo) */                  ;\
265           adc       r2, r2, #0                    /* 64-bit incr (hi) */                  ;\
266           stmia     r0, {r1-r2}                   /* store ev_count */
267 #endif /* __ARMEB__ */
268 #else /* ARM_LOCK_CAS_DEBUG */
269 #define   LOCK_CAS_DEBUG_LOCALS                   /* nothing */
270 #define   LOCK_CAS_DEBUG_COUNT_RESTART  /* nothing */
271 #endif /* ARM_LOCK_CAS_DEBUG */
272 
273 #define   LOCK_CAS_CHECK_LOCALS                                                            \
274 .L_lock_cas:                                                                              ;\
275           .word     _C_LABEL(_lock_cas)                                         ;\
276 .L_lock_cas_end:                                                                ;\
277           .word     _C_LABEL(_lock_cas_end)                                               ;\
278 LOCK_CAS_DEBUG_LOCALS
279 
280 #define   LOCK_CAS_CHECK                                                                   \
281           ldr       r0, [sp]            /* get saved PSR */           ;\
282           and       r0, r0, #(PSR_MODE) /* check for SVC32 mode */    ;\
283           cmp       r0, #(PSR_SVC32_MODE)                                                 ;\
284           bne       99f                           /* nope, get out now */                 ;\
285           ldr       r0, [sp, #(TF_PC)]                                          ;\
286           ldr       r1, .L_lock_cas_end                                         ;\
287           cmp       r0, r1                                                                ;\
288           bge       99f                                                                   ;\
289           ldr       r1, .L_lock_cas                                                       ;\
290           cmp       r0, r1                                                                ;\
291           strgt     r1, [sp, #(TF_PC)]                                          ;\
292           LOCK_CAS_DEBUG_COUNT_RESTART                                          ;\
293 99:
294 
295 #else
296 #define   LOCK_CAS_CHECK                          /* nothing */
297 #define   LOCK_CAS_CHECK_LOCALS                   /* nothing */
298 #endif
299 
300 /*
301  * ASM macros for pushing and pulling trapframes from the stack
302  *
303  * These macros are used to handle the trapframe structure defined above.
304  */
305 
306 /*
307  * PUSHFRAME - macro to push a trap frame on the stack in the current mode
308  * Since the current mode is used, the SVC lr field is not defined.
309  */
310 
311 #ifdef CPU_SA110
312 /*
313  * NOTE: r13 and r14 are stored separately as a work around for the
314  * SA110 rev 2 STM^ bug
315  */
316 #define   PUSHUSERREGS                                                                       \
317           stmia     sp, {r0-r12};                 /* Push the user mode registers */ \
318           add       r0, sp, #(TF_USR_SP-TF_R0); /* Adjust the stack pointer */ \
319           stmia     r0, {r13-r14}^                /* Push the user mode registers */
320 #else
321 #define   PUSHUSERREGS                                                                       \
322           stmia     sp, {r0-r14}^                 /* Push the user mode registers */
323 #endif
324 
325 #define PUSHFRAME                                                                  \
326           str       lr, [sp, #-4]!;               /* Push the return address */    \
327           sub       sp, sp, #(TF_PC-TF_R0);       /* Adjust the stack pointer */             \
328           PUSHUSERREGS;                           /* Push the user mode registers */ \
329           mov     r0, r0;                 /* NOP for previous instruction */ \
330           mrs       r0, spsr;           /* Get the SPSR */               \
331           str       r0, [sp, #-TF_R0]!  /* Push the SPSR on the stack */
332 
333 /*
334  * Push a minimal trapframe so we can dispatch an interrupt from the
335  * idle loop.  The only reason the idle loop wakes up is to dispatch
336  * interrupts so why take the avoid of a full exception when we can do
337  * something minimal.
338  */
339 #define PUSHIDLEFRAME                                                                        \
340           str       lr, [sp, #-4]!;               /* save SVC32 lr */              \
341           str       r6, [sp, #(TF_R6-TF_PC)]!; /* save callee-saved r6 */          \
342           str       r4, [sp, #(TF_R4-TF_R6)]!; /* save callee-saved r4 */          \
343           mrs       r0, cpsr;           /* Get the CPSR */               \
344           str       r0, [sp, #(-TF_R4)]!          /* Push the CPSR on the stack */
345 
346 /*
347  * Push a trapframe to be used by cpu_switchto
348  */
349 #define PUSHSWITCHFRAME(rX)                                                     \
350           mov       ip, sp;                                                               \
351           sub       sp, sp, #(TRAPFRAMESIZE-TF_R12); /* Adjust the stack pointer */ \
352           push      {r4-r11};           /* Push the callee saved registers */ \
353           sub       sp, sp, #TF_R4;               /* reserve rest of trapframe */         \
354           str       ip, [sp, #TF_SVC_SP];                                                 \
355           str       lr, [sp, #TF_SVC_LR];                                                 \
356           str       lr, [sp, #TF_PC];                                           \
357           mrs       rX, cpsr;           /* Get the CPSR */            \
358           str       rX, [sp, #TF_SPSR]  /* save in trapframe */
359 
360 #define PUSHSWITCHFRAME1                                                           \
361           mov       ip, sp;                                                                  \
362           sub       sp, sp, #(TRAPFRAMESIZE-TF_R8); /* Adjust the stack pointer */ \
363           push      {r4-r7};            /* Push some of the callee saved registers */ \
364           sub       sp, sp, #TF_R4;               /* reserve rest of trapframe */         \
365           str       ip, [sp, #TF_SVC_SP];                                                 \
366           str       lr, [sp, #TF_SVC_LR];                                                 \
367           str       lr, [sp, #TF_PC]
368 
369 #if defined(_ARM_ARCH_DWORD_OK) && __ARM_EABI__
370 #define   PUSHSWITCHFRAME2                                                      \
371           strd      r10, [sp, #TF_R10]; /* save r10 & r11 */                    \
372           strd      r8, [sp, #TF_R8];   /* save r8 & r9 */            \
373           mrs       r0, cpsr;           /* Get the CPSR */            \
374           str       r0, [sp, #TF_SPSR]  /* save in trapframe */
375 #else
376 #define   PUSHSWITCHFRAME2                                                      \
377           add       r0, sp, #TF_R8;               /* get ptr to r8 and above */ \
378           stmia     r0, {r8-r11};                 /* save rest of registers */  \
379           mrs       r0, cpsr;           /* Get the CPSR */            \
380           str       r0, [sp, #TF_SPSR]  /* save in trapframe */
381 #endif
382 
383 /*
384  * PULLFRAME - macro to pull a trap frame from the stack in the current mode
385  * Since the current mode is used, the SVC lr field is ignored.
386  */
387 
388 #define PULLFRAME                                                                  \
389           ldr     r0, [sp], #TF_R0;     /* Pop the SPSR from stack */    \
390           msr     spsr_fsxc, r0;                                                             \
391           ldmia   sp, {r0-r14}^;                  /* Restore registers (usr mode) */ \
392           mov     r0, r0;                 /* NOP for previous instruction */ \
393           add       sp, sp, #(TF_PC-TF_R0);       /* Adjust the stack pointer */             \
394           ldr       lr, [sp], #4                  /* Pop the return address */
395 
396 #define PULLIDLEFRAME                                                                        \
397           add       sp, sp, #TF_R4;               /* Adjust the stack pointer */             \
398           ldr       r4, [sp], #(TF_R6-TF_R4); /* restore callee-saved r4 */        \
399           ldr       r6, [sp], #(TF_PC-TF_R6); /* restore callee-saved r6 */        \
400           ldr       lr, [sp], #4                  /* Pop the return address */
401 
402 /*
403  * Pop a trapframe to be used by cpu_switchto (don't touch r0 & r1).
404  */
405 #define PULLSWITCHFRAME                                                                   \
406           add       sp, sp, #TF_R4;               /* Adjust the stack pointer */          \
407           pop       {r4-r11};           /* pop the callee saved registers */ \
408           add       sp, sp, #(TF_PC-TF_R12); /* Adjust the stack pointer */     \
409           ldr       lr, [sp], #4;                 /* pop the return address */
410 
411 /*
412  * PUSHFRAMEINSVC - macro to push a trap frame on the stack in SVC32 mode
413  * This should only be used if the processor is not currently in SVC32
414  * mode. The processor mode is switched to SVC mode and the trap frame is
415  * stored. The SVC lr field is used to store the previous value of
416  * lr in SVC mode.
417  *
418  * NOTE: r13 and r14 are stored separately as a work around for the
419  * SA110 rev 2 STM^ bug
420  */
421 
422 #ifdef _ARM_ARCH_6
423 #define   SET_CPSR_MODE(tmp, mode)      \
424           cps       #(mode)
425 #else
426 #define   SET_CPSR_MODE(tmp, mode)      \
427           mrs     tmp, cpsr;            /* Get the CPSR */               \
428           bic     tmp, tmp, #(PSR_MODE);          /* Fix for SVC mode */                     \
429           orr     tmp, tmp, #(mode);                                               \
430           msr     cpsr_c, tmp           /* Punch into SVC mode */
431 #endif
432 
433 #define PUSHXXXREGSANDSWITCH                                                       \
434           stmdb     sp, {r0-r3};                  /* Save 4 registers */                     \
435           mov       r0, lr;                       /* Save xxx32 r14 */                       \
436           mov       r1, sp;                       /* Save xxx32 sp */              \
437           mrs       r3, spsr;           /* Save xxx32 spsr */                      \
438           SET_CPSR_MODE(r2, PSR_SVC32_MODE)
439 
440 #ifdef KDTRACE_HOOKS
441 #define PUSHDTRACEGAP                                                                        \
442           and       r2, r3, #(PSR_MODE);                                                     \
443           cmp       r2, #(PSR_SVC32_MODE);        /* were we in SVC mode? */       \
444           mov       r2, sp;                                                                  \
445           subeq     r2, r2, #(4 * 16);  /* if so, leave a gap for dtrace */
446 #else
447 #define PUSHDTRACEGAP                                                                        \
448           mov       r2, sp
449 #endif
450 
451 #define PUSHTRAPFRAME(rX)                                                          \
452           bic       r2, rX, #7;                   /* Align new SVC sp */                     \
453           str       r0, [r2, #-4]!;               /* Push return address */        \
454           stmdb     r2!, {sp, lr};                /* Push SVC sp, lr */                      \
455           mov       sp, r2;                       /* Keep stack aligned */         \
456           msr     spsr_fsxc, r3;                  /* Restore correct spsr */       \
457           ldmdb     r1, {r0-r3};                  /* Restore 4 regs from xxx mode */ \
458           sub       sp, sp, #(TF_SVC_SP-TF_R0); /* Adjust the stack pointer */ \
459           PUSHUSERREGS;                           /* Push the user mode registers */ \
460           mov     r0, r0;                 /* NOP for previous instruction */ \
461           mrs       r0, spsr;           /* Get the SPSR */               \
462           str       r0, [sp, #-TF_R0]!  /* Push the SPSR onto the stack */
463 
464 #define PUSHFRAMEINSVC                                                                       \
465           PUSHXXXREGSANDSWITCH;                                                              \
466           PUSHTRAPFRAME(sp)
467 
468 /*
469  * PULLFRAMEFROMSVCANDEXIT - macro to pull a trap frame from the stack
470  * in SVC32 mode and restore the saved processor mode and PC.
471  * This should be used when the SVC lr register needs to be restored on
472  * exit.
473  */
474 
475 #define PULLFRAMEFROMSVCANDEXIT                                                              \
476           ldr     r0, [sp], #TF_R0;     /* Pop the SPSR from stack */    \
477           msr     spsr_fsxc, r0;                  /* restore SPSR */               \
478           ldmia   sp, {r0-r14}^;                  /* Restore registers (usr mode) */ \
479           mov     r0, r0;                         /* NOP for previous instruction */ \
480           add       sp, sp, #(TF_SVC_SP-TF_R0); /* Adjust the stack pointer */ \
481           ldmia     sp, {sp, lr, pc}^   /* Restore lr and exit */
482 
483 #endif /* _LOCORE */
484 
485 #endif /* _ARM32_FRAME_H_ */
486