| 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_ */ |